diff options
Diffstat (limited to 'sw/source')
160 files changed, 3126 insertions, 1245 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/swrect.cxx b/sw/source/core/bastyp/swrect.cxx index 1d53e6e7a71e..884c155003e2 100644 --- a/sw/source/core/bastyp/swrect.cxx +++ b/sw/source/core/bastyp/swrect.cxx @@ -19,6 +19,8 @@ #include <swrect.hxx> +#include <libxml/xmlwriter.h> + #ifdef DBG_UTIL #include <tools/stream.hxx> #endif @@ -218,6 +220,16 @@ void SwRect::SetUpperRightCorner( const Point& rNew ) void SwRect::SetLowerLeftCorner( const Point& rNew ) { m_Point = Point(rNew.X(), rNew.Y() - m_Size.getHeight()); } +void SwRect::dumpAsXmlAttributes(xmlTextWriterPtr writer) const +{ + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("left"), "%li", Left()); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("top"), "%li", Top()); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("width"), "%li", Width()); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("height"), "%li", Height()); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("bottom"), "%li", Bottom()); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("right"), "%li", Right()); +} + #ifdef DBG_UTIL SvStream& WriteSwRect(SvStream &rStream, const SwRect &rRect) { diff --git a/sw/source/core/crsr/annotationmark.cxx b/sw/source/core/crsr/annotationmark.cxx index ea11fad3d2cc..1deeb7e1f067 100644 --- a/sw/source/core/crsr/annotationmark.cxx +++ b/sw/source/core/crsr/annotationmark.cxx @@ -54,7 +54,7 @@ namespace sw { namespace mark SwTextNode *pTextNode = GetMarkEnd().nNode.GetNode().GetTextNode(); assert(pTextNode); SwTextField *const pTextField = pTextNode->GetFieldTextAttrAt( - GetMarkEnd().nContent.GetIndex()-1, true); + GetMarkEnd().nContent.GetIndex()-1, ::sw::GetTextAttrMode::Default); assert(pTextField != nullptr); auto pPostItField = dynamic_cast<const SwPostItField*>(pTextField->GetFormatField().GetField()); diff --git a/sw/source/core/crsr/bookmrk.cxx b/sw/source/core/crsr/bookmrk.cxx index a16713dc295d..fad65a238147 100644 --- a/sw/source/core/crsr/bookmrk.cxx +++ b/sw/source/core/crsr/bookmrk.cxx @@ -25,6 +25,7 @@ #include <doc.hxx> #include <ndtxt.hxx> #include <pam.hxx> +#include <hints.hxx> #include <swserv.hxx> #include <sfx2/linkmgr.hxx> #include <UndoBookmark.hxx> @@ -238,6 +239,12 @@ namespace io_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::UI_REPLACE, nullptr); }; + + auto InvalidatePosition(SwPosition const& rPos) -> void + { + SwUpdateAttr const hint(rPos.nContent.GetIndex(), rPos.nContent.GetIndex(), 0); + rPos.nNode.GetNode().GetTextNode()->NotifyClients(nullptr, &hint); + } } namespace sw { namespace mark @@ -337,6 +344,11 @@ namespace sw { namespace mark } // TODO: everything else uses MarkBase::GenerateNewName ? + + auto MarkBase::InvalidateFrames() -> void + { + } + NavigatorReminder::NavigatorReminder(const SwPaM& rPaM) : MarkBase(rPaM, "__NavigatorReminder__") { } @@ -393,6 +405,7 @@ namespace sw { namespace mark std::make_unique<SwUndoInsBookmark>(*this)); } io_pDoc->getIDocumentState().SetModified(); + InvalidateFrames(); } void Bookmark::DeregisterFromDoc(SwDoc* const io_pDoc) @@ -405,6 +418,36 @@ namespace sw { namespace mark std::make_unique<SwUndoDeleteBookmark>(*this)); } io_pDoc->getIDocumentState().SetModified(); + InvalidateFrames(); + } + + // invalidate text frames in case it's hidden or Formatting Marks enabled + auto Bookmark::InvalidateFrames() -> void + { + InvalidatePosition(GetMarkPos()); + if (IsExpanded()) + { + InvalidatePosition(GetOtherMarkPos()); + } + } + + void Bookmark::Hide(bool const isHide) + { + if (isHide != m_bHidden) + { + m_bHidden = isHide; + InvalidateFrames(); + } + } + + void Bookmark::SetHideCondition(OUString const& rHideCondition) + { + if (m_sHideCondition != rHideCondition) + { + m_sHideCondition = rHideCondition; + // don't eval condition here yet - probably only needed for + // UI editing condition and that doesn't exist yet + } } ::sfx2::IXmlIdRegistry& Bookmark::GetRegistry() @@ -513,6 +556,8 @@ namespace sw { namespace mark if (eMode == sw::mark::InsertMode::New) { lcl_SetFieldMarks(this, io_pDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND, pSepPos); + // no need to invalidate text frames here, the insertion of the + // CH_TXT_ATR already invalidates } else { 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 0d8d273f34be..bf3f1718e67c 100644 --- a/sw/source/core/crsr/crsrsh.cxx +++ b/sw/source/core/crsr/crsrsh.cxx @@ -2244,7 +2244,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) diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx index 7df0ce6ff323..0c53e6448b46 100644 --- a/sw/source/core/crsr/crstrvl.cxx +++ b/sw/source/core/crsr/crstrvl.cxx @@ -774,7 +774,7 @@ bool SwCursorShell::MoveFieldType( SwTextNode* pTNd = rPos.nNode.GetNode().GetTextNode(); OSL_ENSURE( pTNd, "No ContentNode" ); - SwTextField * pTextField = pTNd->GetFieldTextAttrAt( rPos.nContent.GetIndex(), true ); + SwTextField * pTextField = pTNd->GetFieldTextAttrAt(rPos.nContent.GetIndex(), ::sw::GetTextAttrMode::Default); const bool bDelField = ( pTextField == nullptr ); sal_Int32 nContentOffset = -1; @@ -878,14 +878,14 @@ bool SwCursorShell::GotoFormatField( const SwFormatField& rField ) SwTextField * SwCursorShell::GetTextFieldAtPos( const SwPosition* pPos, - const bool bIncludeInputFieldAtStart ) + ::sw::GetTextAttrMode const eMode) { SwTextField* pTextField = nullptr; SwTextNode * const pNode = pPos->nNode.GetNode().GetTextNode(); if ( pNode != nullptr ) { - pTextField = pNode->GetFieldTextAttrAt( pPos->nContent.GetIndex(), bIncludeInputFieldAtStart ); + pTextField = pNode->GetFieldTextAttrAt( pPos->nContent.GetIndex(), eMode); } return pTextField; @@ -893,11 +893,11 @@ SwTextField * SwCursorShell::GetTextFieldAtPos( SwTextField* SwCursorShell::GetTextFieldAtCursor( const SwPaM* pCursor, - const bool bIncludeInputFieldAtStart ) + ::sw::GetTextAttrMode const eMode) { SwTextField* pFieldAtCursor = nullptr; - SwTextField* pTextField = GetTextFieldAtPos( pCursor->Start(), bIncludeInputFieldAtStart ); + SwTextField* pTextField = GetTextFieldAtPos(pCursor->Start(), eMode); if ( pTextField != nullptr && pCursor->Start()->nNode == pCursor->End()->nNode ) { @@ -918,7 +918,8 @@ SwField* SwCursorShell::GetFieldAtCursor( const SwPaM *const pCursor, const bool bIncludeInputFieldAtStart) { - SwTextField *const pField(GetTextFieldAtCursor(pCursor, bIncludeInputFieldAtStart)); + SwTextField *const pField(GetTextFieldAtCursor(pCursor, + bIncludeInputFieldAtStart ? ::sw::GetTextAttrMode::Default : ::sw::GetTextAttrMode::Expand)); return pField ? const_cast<SwField*>(pField->GetFormatField().GetField()) : nullptr; @@ -949,7 +950,7 @@ bool SwCursorShell::CursorInsideInputField() const { for(SwPaM& rCursor : GetCursor()->GetRingContainer()) { - if (dynamic_cast<const SwTextInputField*>(GetTextFieldAtCursor(&rCursor, true))) + if (dynamic_cast<const SwTextInputField*>(GetTextFieldAtCursor(&rCursor, ::sw::GetTextAttrMode::Parent))) return true; } return false; @@ -957,7 +958,7 @@ bool SwCursorShell::CursorInsideInputField() const bool SwCursorShell::PosInsideInputField( const SwPosition& rPos ) { - return dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos( &rPos, false )) != nullptr; + return dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos(&rPos, ::sw::GetTextAttrMode::Parent)) != nullptr; } bool SwCursorShell::DocPtInsideInputField( const Point& rDocPt ) const @@ -973,7 +974,7 @@ bool SwCursorShell::DocPtInsideInputField( const Point& rDocPt ) const sal_Int32 SwCursorShell::StartOfInputFieldAtPos( const SwPosition& rPos ) { - const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos( &rPos, true )); + const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos(&rPos, ::sw::GetTextAttrMode::Default)); assert(pTextInputField != nullptr && "<SwEditShell::StartOfInputFieldAtPos(..)> - no Input Field at given position"); return pTextInputField->GetStart(); @@ -981,7 +982,7 @@ sal_Int32 SwCursorShell::StartOfInputFieldAtPos( const SwPosition& rPos ) sal_Int32 SwCursorShell::EndOfInputFieldAtPos( const SwPosition& rPos ) { - const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos( &rPos, true )); + const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos(&rPos, ::sw::GetTextAttrMode::Default)); assert(pTextInputField != nullptr && "<SwEditShell::EndOfInputFieldAtPos(..)> - no Input Field at given position"); return *(pTextInputField->End()); @@ -1930,7 +1931,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 +1955,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 ) @@ -1970,14 +2008,14 @@ bool SwCursorShell::SelectTextAttr( sal_uInt16 nWhich, pTextAttr = pTextNd ? pTextNd->GetTextAttrAt(rPos.nContent.GetIndex(), nWhich, - bExpand ? SwTextNode::EXPAND : SwTextNode::DEFAULT) + bExpand ? ::sw::GetTextAttrMode::Expand : ::sw::GetTextAttrMode::Default) : nullptr; } 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/swcrsr.cxx b/sw/source/core/crsr/swcrsr.cxx index 19d47dcec68e..98a3b9682442 100644 --- a/sw/source/core/crsr/swcrsr.cxx +++ b/sw/source/core/crsr/swcrsr.cxx @@ -210,7 +210,7 @@ namespace SwTextNode* pTextNd = pPos->nNode.GetNode().GetTextNode(); if (!pTextNd) return nullptr; - return pTextNd->GetTextAttrAt(pPos->nContent.GetIndex(), RES_TXTATR_INPUTFIELD, SwTextNode::PARENT); + return pTextNd->GetTextAttrAt(pPos->nContent.GetIndex(), RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent); } } @@ -1790,7 +1790,7 @@ bool SwCursor::LeftRight( bool bLeft, sal_uInt16 nCnt, sal_uInt16 nMode, SwTextNode const*const pNode(GetPoint()->nNode.GetNode().GetTextNode()); assert(pNode); SwTextAttr const*const pInputField(pNode->GetTextAttrAt( - GetPoint()->nContent.GetIndex(), RES_TXTATR_INPUTFIELD, SwTextNode::PARENT)); + GetPoint()->nContent.GetIndex(), RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent)); if (pInputField) { continue; // skip over input fields diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx index 5aa804d364b0..4c4bbd682056 100644 --- a/sw/source/core/crsr/viscrs.cxx +++ b/sw/source/core/crsr/viscrs.cxx @@ -422,7 +422,7 @@ void SwSelPaintRects::HighlightInputField() if (m_bShowTextInputFieldOverlay) { SwTextInputField* pCurTextInputFieldAtCursor = - dynamic_cast<SwTextInputField*>(SwCursorShell::GetTextFieldAtPos( GetShell()->GetCursor()->Start(), false )); + dynamic_cast<SwTextInputField*>(SwCursorShell::GetTextFieldAtPos( GetShell()->GetCursor()->Start(), ::sw::GetTextAttrMode::Expand)); if ( pCurTextInputFieldAtCursor != nullptr ) { SwTextNode* pTextNode = pCurTextInputFieldAtCursor->GetpTextNode(); diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx index 0199260aa6cf..975a35965d17 100644 --- a/sw/source/core/doc/DocumentContentOperationsManager.cxx +++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx @@ -387,7 +387,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 +618,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 +628,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 +652,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 +662,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 +937,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); } @@ -1994,7 +1998,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,7 +2009,7 @@ void DocumentContentOperationsManager::DeleteDummyChar( void DocumentContentOperationsManager::DeleteRange( SwPaM & rPam ) { - lcl_DoWithBreaks( *this, rPam, &DocumentContentOperationsManager::DeleteRangeImpl ); + lcl_DoWithBreaks(*this, rPam, SwDeleteFlags::Default, &DocumentContentOperationsManager::DeleteRangeImpl); if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()) @@ -2108,7 +2112,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 ); @@ -2131,6 +2135,37 @@ bool DocumentContentOperationsManager::DelFullPara( SwPaM& rPam ) return false; } } + + // must delete all fieldmarks before CorrAbs(), or they'll remain + // moved to wrong node without their CH_TXT_ATR_FIELD* + // (note: deleteMarks() doesn't help here, in case of partially + // selected fieldmarks; let's delete these as re-inserting their chars + // elsewhere looks difficult) + ::std::set<::sw::mark::IFieldmark*> fieldmarks; + for (SwNodeIndex i = aRg.aStart; i <= aRg.aEnd; ++i) + { + if (SwTextNode *const pTextNode = i.GetNode().GetTextNode()) + { + for (sal_Int32 j = 0; j < pTextNode->GetText().getLength(); ++j) + { + switch (pTextNode->GetText()[j]) + { + case CH_TXT_ATR_FIELDSTART: + case CH_TXT_ATR_FIELDEND: + fieldmarks.insert(m_rDoc.getIDocumentMarkAccess()->getFieldmarkAt(SwPosition(*pTextNode, j))); + break; + case CH_TXT_ATR_FIELDSEP: + fieldmarks.insert(m_rDoc.getIDocumentMarkAccess()->getFieldmarkFor(SwPosition(*pTextNode, j))); + break; + } + } + } + } + for (auto const pFieldMark : fieldmarks) + { + m_rDoc.getIDocumentMarkAccess()->deleteMark(pFieldMark); + } + // move bookmarks, redlines etc. if (aRg.aStart == aRg.aEnd) // only first CorrAbs variant handles this { @@ -2172,13 +2207,13 @@ 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 ); @@ -3311,8 +3346,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); @@ -3447,21 +3482,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 @@ -3873,7 +3915,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()); @@ -3953,7 +3995,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 @@ -4014,7 +4056,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; @@ -4026,7 +4068,7 @@ bool DocumentContentOperationsManager::DeleteAndJoinImpl( SwPaM & rPam, } { - bool const bSuccess( DeleteRangeImpl( rPam ) ); + bool const bSuccess( DeleteRangeImpl(rPam, flags) ); if (!bSuccess) return false; } @@ -4045,14 +4087,14 @@ 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() ); - 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(); @@ -4061,7 +4103,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(); @@ -4126,7 +4168,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(); @@ -4138,8 +4180,11 @@ 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, @@ -4272,7 +4317,7 @@ bool DocumentContentOperationsManager::ReplaceRangeImpl( SwPaM& rPam, const OUSt // the other views out of the deletion range. // Except for itself! SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() ); - ::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() ); + ::PaMCorrAbs( aDelPam, *aDelPam.End() ); SwPosition *pStt = aDelPam.Start(), *pEnd = aDelPam.End(); @@ -4376,12 +4421,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()) @@ -4404,8 +4463,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; diff --git a/sw/source/core/doc/DocumentFieldsManager.cxx b/sw/source/core/doc/DocumentFieldsManager.cxx index 134d8cab9968..b696734c6899 100644 --- a/sw/source/core/doc/DocumentFieldsManager.cxx +++ b/sw/source/core/doc/DocumentFieldsManager.cxx @@ -1052,6 +1052,17 @@ void DocumentFieldsManager::UpdateExpFieldsImpl( } continue; } + ::sw::mark::IBookmark *const pBookmark( + const_cast<::sw::mark::IBookmark *>(it->GetBookmark())); + if (pBookmark) + { + SwSbxValue const aValue(aCalc.Calculate(pBookmark->GetHideCondition())); + if (!aValue.IsVoidValue()) + { + pBookmark->Hide(aValue.GetBool()); + } + continue; + } SwTextField* pTextField = const_cast<SwTextField*>(it->GetTextField()); if( !pTextField ) @@ -1743,7 +1754,7 @@ SwTextField * DocumentFieldsManager::GetTextFieldAtPos(const SwPosition & rPos) SwTextNode * const pNode = rPos.nNode.GetNode().GetTextNode(); return (pNode != nullptr) - ? pNode->GetFieldTextAttrAt( rPos.nContent.GetIndex(), true ) + ? pNode->GetFieldTextAttrAt(rPos.nContent.GetIndex(), ::sw::GetTextAttrMode::Default) : nullptr; } diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx b/sw/source/core/doc/DocumentRedlineManager.cxx index ff9b05743d10..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); @@ -301,6 +307,8 @@ void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam) pFrame->SetMergedPara(sw::CheckParaRedlineMerge( *pFrame, rFirstNode, eMode)); eMode = sw::FrameMode::New; // Existing is not idempotent! + // update pNode so MakeFrames starts on 2nd node + pNode = &rFirstNode; } } if (pLast != pNode) @@ -2317,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/DocumentStylePoolManager.cxx b/sw/source/core/doc/DocumentStylePoolManager.cxx index 767af3a22010..b5bb128629e6 100644 --- a/sw/source/core/doc/DocumentStylePoolManager.cxx +++ b/sw/source/core/doc/DocumentStylePoolManager.cxx @@ -1588,7 +1588,7 @@ SwFormat* DocumentStylePoolManager::GetFormatFromPool( sal_uInt16 nId ) aSet.Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER, text::RelOrientation::PRINT_AREA ) ); aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::PRINT_AREA ) ); Color aCol( COL_BLACK ); - SvxBorderLine aLine( &aCol, DEF_LINE_WIDTH_0 ); + SvxBorderLine aLine( &aCol, SvxBorderLineWidth::Hairline ); SvxBoxItem aBox( RES_BOX ); aBox.SetLine( &aLine, SvxBoxItemLine::TOP ); aBox.SetLine( &aLine, SvxBoxItemLine::BOTTOM ); diff --git a/sw/source/core/doc/docbm.cxx b/sw/source/core/doc/docbm.cxx index a9eed445a21c..e9fde5afcd5e 100644 --- a/sw/source/core/doc/docbm.cxx +++ b/sw/source/core/doc/docbm.cxx @@ -561,6 +561,18 @@ namespace sw { namespace mark pPos2->nContent.GetIndex()); } #endif + if ( (!rPaM.GetPoint()->nNode.GetNode().IsTextNode() + && (eType != MarkType::UNO_BOOKMARK + // SwXTextRange can be on table node or plain start node (FLY_AT_FLY) + || !rPaM.GetPoint()->nNode.GetNode().IsStartNode())) + || (!rPaM.GetMark()->nNode.GetNode().IsTextNode() + && (eType != MarkType::UNO_BOOKMARK + || !rPaM.GetMark()->nNode.GetNode().IsStartNode()))) + { + SAL_WARN("sw.core", "MarkManager::makeMark(..)" + " - refusing to create mark on non-textnode"); + return nullptr; + } // There should only be one CrossRefBookmark per Textnode per Type if ((eType == MarkType::CROSSREF_NUMITEM_BOOKMARK || eType == MarkType::CROSSREF_HEADING_BOOKMARK) && (lcl_FindMarkAtPos(m_vBookmarks, *rPaM.Start(), eType) != m_vBookmarks.end())) @@ -809,6 +821,8 @@ namespace sw { namespace mark if (!pMarkBase) return; + pMarkBase->InvalidateFrames(); + pMarkBase->SetMarkPos(*(rPaM.GetPoint())); if(rPaM.HasMark()) pMarkBase->SetOtherMarkPos(*(rPaM.GetMark())); @@ -818,6 +832,8 @@ namespace sw { namespace mark if(pMarkBase->GetMarkPos() != pMarkBase->GetMarkStart()) pMarkBase->Swap(); + pMarkBase->InvalidateFrames(); + sortMarks(); } @@ -1111,7 +1127,7 @@ namespace sw { namespace mark pppMark != vMarksToDelete.rend(); ++pppMark ) { - vDelay.push_back(deleteMark(*pppMark)); + vDelay.push_back(deleteMark(*pppMark, pSaveBkmk != nullptr)); } } // scope to kill vDelay @@ -1130,8 +1146,9 @@ namespace sw { namespace mark { std::unique_ptr<Fieldmark> m_pFieldmark; SwDoc * m_pDoc; - LazyFieldmarkDeleter(Fieldmark* pMark, SwDoc *const pDoc) - : m_pFieldmark(pMark), m_pDoc(pDoc) + bool const m_isMoveNodes; + LazyFieldmarkDeleter(Fieldmark* pMark, SwDoc *const pDoc, bool const isMoveNodes) + : m_pFieldmark(pMark), m_pDoc(pDoc), m_isMoveNodes(isMoveNodes) { assert(m_pFieldmark); } @@ -1141,12 +1158,15 @@ namespace sw { namespace mark // command *cannot* be deleted here as it would create a separate // SwUndoDelete that's interleaved with the SwHistory of the outer // one - only delete the CH_TXT_ATR_FIELD*! - m_pFieldmark->ReleaseDoc(m_pDoc); + if (!m_isMoveNodes) + { + m_pFieldmark->ReleaseDoc(m_pDoc); + } } }; std::unique_ptr<IDocumentMarkAccess::ILazyDeleter> - MarkManager::deleteMark(const const_iterator_t& ppMark) + MarkManager::deleteMark(const const_iterator_t& ppMark, bool const isMoveNodes) { std::unique_ptr<ILazyDeleter> ret; if (ppMark.get() == m_vAllMarks.end()) @@ -1184,7 +1204,7 @@ namespace sw { namespace mark ClearFieldActivation(); m_vFieldmarks.erase(ppFieldmark); - ret.reset(new LazyFieldmarkDeleter(dynamic_cast<Fieldmark*>(pMark), m_pDoc)); + ret.reset(new LazyFieldmarkDeleter(dynamic_cast<Fieldmark*>(pMark), m_pDoc, isMoveNodes)); } else { @@ -1241,7 +1261,7 @@ namespace sw { namespace mark for ( ; it != endIt; ++it) if (*it == pMark) { - deleteMark(iterator(it)); + deleteMark(iterator(it), false); break; } } @@ -1340,7 +1360,7 @@ namespace sw { namespace mark if (!pFieldmark) return; - deleteMark(lcl_FindMark(m_vAllMarks, pFieldmark)); + deleteMark(lcl_FindMark(m_vAllMarks, pFieldmark), false); } ::sw::mark::IFieldmark* MarkManager::changeFormFieldmarkType(::sw::mark::IFieldmark* pFieldmark, const OUString& rNewType) @@ -1703,7 +1723,7 @@ void SaveBookmark::SetInDoc( { ::sw::mark::IBookmark* const pBookmark = dynamic_cast<::sw::mark::IBookmark*>( pDoc->getIDocumentMarkAccess()->makeMark(aPam, m_aName, - m_eOrigBkmType, sw::mark::InsertMode::New)); + m_eOrigBkmType, sw::mark::InsertMode::CopyText)); if(pBookmark) { pBookmark->SetKeyCode(m_aCode); diff --git a/sw/source/core/doc/docchart.cxx b/sw/source/core/doc/docchart.cxx index b602c92afd14..f31dd1f73088 100644 --- a/sw/source/core/doc/docchart.cxx +++ b/sw/source/core/doc/docchart.cxx @@ -110,9 +110,30 @@ void SwDoc::UpdateCharts_( const SwTable& rTable, SwViewShell const & rVSh ) con aName == pONd->GetChartTableName() && pONd->getLayoutFrame( rVSh.GetLayout() ) ) { + // tdf#122995 for OLE/Charts in SW we do not (yet) have a refresh + // mechanism or embedding of the primitive representation, so this + // needs to be done locally here (simplest solution). + bool bImmediateMode(false); + + if(pONd->IsChart()) + { + // refresh to trigger repaint + const SwRect aChartRect(pONd->FindLayoutRect()); + if(!aChartRect.IsEmpty()) + const_cast<SwViewShell &>(rVSh).InvalidateWindows(aChartRect); + + // forced refresh of the chart's primitive representation + pONd->GetOLEObj().resetBufferedData(); + + // InvalidateTable using the Immediate-Mode, else the chart will + // not yet know that it is invalidated at the next repaint and create + // the same graphical representation again + bImmediateMode = true; + } + SwChartDataProvider *pPCD = getIDocumentChartDataProviderAccess().GetChartDataProvider(); if (pPCD) - pPCD->InvalidateTable( &rTable ); + pPCD->InvalidateTable( &rTable, bImmediateMode ); // following this the framework will now take care of repainting // the chart or it's replacement image... } diff --git a/sw/source/core/doc/docedt.cxx b/sw/source/core/doc/docedt.cxx index 1b93a7a56a78..d6072b4b3725 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) diff --git a/sw/source/core/doc/docfld.cxx b/sw/source/core/doc/docfld.cxx index 8b5f7d467328..c027003c4e97 100644 --- a/sw/source/core/doc/docfld.cxx +++ b/sw/source/core/doc/docfld.cxx @@ -26,6 +26,7 @@ #include <unotools/transliterationwrapper.hxx> #include <doc.hxx> #include <IDocumentFieldsAccess.hxx> +#include <IDocumentMarkAccess.hxx> #include <IDocumentState.hxx> #include <IDocumentLayoutAccess.hxx> #include <cntfrm.hxx> @@ -103,6 +104,24 @@ SetGetExpField::SetGetExpField( const SwSectionNode& rSectNd, } } +SetGetExpField::SetGetExpField(::sw::mark::IBookmark const& rBookmark, + SwPosition const*const pPos) +{ + m_eSetGetExpFieldType = BOOKMARK; + m_CNTNT.pBookmark = &rBookmark; + + if (pPos) + { + m_nNode = pPos->nNode.GetIndex(); + m_nContent = pPos->nContent.GetIndex(); + } + else + { + m_nNode = rBookmark.GetMarkStart().nNode.GetIndex(); + m_nContent = rBookmark.GetMarkStart().nContent.GetIndex();; + } +} + SetGetExpField::SetGetExpField( const SwTableBox& rTBox ) { m_eSetGetExpFieldType = TABLEBOX; @@ -272,6 +291,10 @@ const SwNode* SetGetExpField::GetNodeFromContent() const pRet = m_CNTNT.pSection->GetFormat()->GetSectionNode(); break; + case BOOKMARK: + pRet = &m_CNTNT.pBookmark->GetMarkStart().nNode.GetNode(); + break; + case CRSRPOS: pRet = &m_CNTNT.pPos->nNode.GetNode(); break; @@ -313,6 +336,9 @@ sal_Int32 SetGetExpField::GetCntPosFromContent() const case TEXTTOXMARK: nRet = m_CNTNT.pTextTOX->GetStart(); break; + case BOOKMARK: + nRet = m_CNTNT.pBookmark->GetMarkStart().nContent.GetIndex(); + break; case CRSRPOS: nRet = m_CNTNT.pPos->nContent.GetIndex(); break; @@ -863,7 +889,20 @@ void SwDocUpdateField::MakeFieldList_( SwDoc& rDoc, int eGetMode ) // add all to the list so that they are sorted for (const auto &nId : aTmpArr) { - GetBodyNode( *rDoc.GetNodes()[ nId ]->GetSectionNode() ); + SwSectionNode const& rSectionNode(*rDoc.GetNodes()[ nId ]->GetSectionNode()); + GetBodyNodeGeneric(rSectionNode, rSectionNode); + } + + // bookmarks with hide conditions, handle similar to sections + auto const& rIDMA(*rDoc.getIDocumentMarkAccess()); + for (auto it = rIDMA.getBookmarksBegin(); it != rIDMA.getBookmarksEnd(); ++it) + { + auto const pBookmark(dynamic_cast<::sw::mark::IBookmark const*>(*it)); + assert(pBookmark); + if (!pBookmark->GetHideCondition().isEmpty()) + { + GetBodyNodeGeneric((*it)->GetMarkStart().nNode.GetNode(), *pBookmark); + } } } @@ -1040,19 +1079,22 @@ void SwDocUpdateField::GetBodyNode( const SwTextField& rTField, SwFieldIds nFiel m_pFieldSortList->insert( std::move(pNew) ); } -void SwDocUpdateField::GetBodyNode( const SwSectionNode& rSectNd ) +template<typename T> +void SwDocUpdateField::GetBodyNodeGeneric(SwNode const& rNode, T const& rCond) { - const SwDoc& rDoc = *rSectNd.GetDoc(); + const SwDoc& rDoc = *rNode.GetDoc(); std::unique_ptr<SetGetExpField> pNew; - if( rSectNd.GetIndex() < rDoc.GetNodes().GetEndOfExtras().GetIndex() ) + if (rNode.GetIndex() < rDoc.GetNodes().GetEndOfExtras().GetIndex()) { do { // middle check loop // we need to get the anchor first // create index to determine the TextNode - SwPosition aPos( rSectNd ); - SwContentNode* pCNd = rDoc.GetNodes().GoNext( &aPos.nNode ); // to the next ContentNode + SwPosition aPos(rNode); + SwContentNode const*const pCNd = rNode.IsSectionNode() + ? rDoc.GetNodes().GoNext(&aPos.nNode) // to the next ContentNode + : rNode.GetContentNode(); if( !pCNd || !pCNd->IsTextNode() ) break; @@ -1068,13 +1110,13 @@ void SwDocUpdateField::GetBodyNode( const SwSectionNode& rSectNd ) bool const bResult = GetBodyTextNode( rDoc, aPos, *pFrame ); OSL_ENSURE(bResult, "where is the Field"); - pNew.reset(new SetGetExpField( rSectNd, &aPos )); + pNew.reset(new SetGetExpField(rCond, &aPos)); } while( false ); } if( !pNew ) - pNew.reset(new SetGetExpField( rSectNd )); + pNew.reset(new SetGetExpField(rCond)); m_pFieldSortList->insert( std::move(pNew) ); } 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/docredln.cxx b/sw/source/core/doc/docredln.cxx index ee8e8b23d8c0..66c6af16f4c9 100644 --- a/sw/source/core/doc/docredln.cxx +++ b/sw/source/core/doc/docredln.cxx @@ -1784,7 +1784,7 @@ OUString SwRangeRedline::GetDescr() OUString sDescr = DenoteSpecialCharacters(pPaM->GetText()); if (const SwTextNode *pTextNode = pPaM->GetNode().GetTextNode()) { - if (const SwTextAttr* pTextAttr = pTextNode->GetFieldTextAttrAt(pPaM->GetPoint()->nContent.GetIndex() - 1, true )) + if (const SwTextAttr* pTextAttr = pTextNode->GetFieldTextAttrAt(pPaM->GetPoint()->nContent.GetIndex() - 1, ::sw::GetTextAttrMode::Default)) { sDescr = SwResId(STR_START_QUOTE) + pTextAttr->GetFormatField().GetField()->GetFieldName() diff --git a/sw/source/core/doc/tblafmt.cxx b/sw/source/core/doc/tblafmt.cxx index 35e626a0f5a1..3ff7ef02b6d0 100644 --- a/sw/source/core/doc/tblafmt.cxx +++ b/sw/source/core/doc/tblafmt.cxx @@ -1021,7 +1021,7 @@ SwTableAutoFormatTable::SwTableAutoFormatTable() SvxBoxItem aBox( RES_BOX ); aBox.SetAllDistances(55); - SvxBorderLine aLn( &aColor, DEF_LINE_WIDTH_5 ); + SvxBorderLine aLn( &aColor, SvxBorderLineWidth::VeryThin ); aBox.SetLine( &aLn, SvxBoxItemLine::LEFT ); aBox.SetLine( &aLn, SvxBoxItemLine::BOTTOM ); 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..6154cfc7d776 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(); diff --git a/sw/source/core/docnode/ndtbl.cxx b/sw/source/core/docnode/ndtbl.cxx index 15a49729ce51..e6731feba860 100644 --- a/sw/source/core/docnode/ndtbl.cxx +++ b/sw/source/core/docnode/ndtbl.cxx @@ -114,11 +114,11 @@ static void lcl_SetDfltBoxAttr( SwFrameFormat& rFormat, sal_uInt8 nId ) const bool bHTML = rFormat.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE); Color aCol( bHTML ? COL_GRAY : COL_BLACK ); - SvxBorderLine aLine( &aCol, DEF_LINE_WIDTH_0 ); + // Default border in Writer: 0.5pt (matching Word) + SvxBorderLine aLine( &aCol, SvxBorderLineWidth::VeryThin ); if ( bHTML ) { aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE); - aLine.SetWidth( DEF_LINE_WIDTH_0 ); } SvxBoxItem aBox(RES_BOX); aBox.SetAllDistances(55); @@ -2040,7 +2040,7 @@ bool SwDoc::DeleteRowCol( const SwSelBoxes& rBoxes, bool bColumn ) bSavePageBreak = true; } } - std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete( aPaM )); + std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete(aPaM, SwDeleteFlags::Default)); if( bNewTextNd ) pUndo->SetTableDelLastNd(); pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc ); 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..28088536c7ba 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; @@ -2035,90 +2036,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 +2145,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 +2210,9 @@ SwNode* SwNodes::FindPrvNxtFrameNode( SwNodeIndex& rFrameIdx, { rFrameIdx = aIdx; pFrameNd = &aIdx.GetNode(); + assert(!"this isn't dead code?"); } } - } } } } diff --git a/sw/source/core/draw/dcontact.cxx b/sw/source/core/draw/dcontact.cxx index 9d057bcabc7d..5f05c4d21cd7 100644 --- a/sw/source/core/draw/dcontact.cxx +++ b/sw/source/core/draw/dcontact.cxx @@ -476,8 +476,28 @@ SwFlyDrawContact::~SwFlyDrawContact() } } -sal_uInt32 SwFlyDrawContact::GetOrdNumForNewRef(const SwFlyFrame* pFly) +sal_uInt32 SwFlyDrawContact::GetOrdNumForNewRef(const SwFlyFrame* pFly, + SwFrame const& rAnchorFrame) { + // maintain invariant that a shape's textbox immediately follows the shape + // also for the multiple SdrVirtObj created for shapes in header/footer + if (SwFrameFormat const*const pDrawFormat = + SwTextBoxHelper::getOtherTextBoxFormat(GetFormat(), RES_FLYFRMFMT)) + { + // assume that the draw SdrVirtObj is always created before the flyframe one + if (SwSortedObjs const*const pObjs = rAnchorFrame.GetDrawObjs()) + { + for (SwAnchoredObject const*const pAnchoredObj : *pObjs) + { + if (&pAnchoredObj->GetFrameFormat() == pDrawFormat) + { + return pAnchoredObj->GetDrawObj()->GetOrdNum() + 1; + } + } + } + // if called from AppendObjs(), this is a problem; if called from lcl_SetFlyFrameAttr() it's not + SAL_INFO("sw", "GetOrdNumForNewRef: cannot find SdrObject for text box's shape"); + } // search for another Writer fly frame registered at same frame format SwIterator<SwFlyFrame,SwFormat> aIter(*GetFormat()); const SwFlyFrame* pFlyFrame(nullptr); @@ -499,7 +519,8 @@ sal_uInt32 SwFlyDrawContact::GetOrdNumForNewRef(const SwFlyFrame* pFly) return GetMaster()->GetOrdNumDirect(); } -SwVirtFlyDrawObj* SwFlyDrawContact::CreateNewRef(SwFlyFrame* pFly, SwFlyFrameFormat* pFormat) +SwVirtFlyDrawObj* SwFlyDrawContact::CreateNewRef(SwFlyFrame* pFly, + SwFlyFrameFormat* pFormat, SwFrame const& rAnchorFrame) { // Find ContactObject from the Format. If there's already one, we just // need to create a new Ref, else we create the Contact now. @@ -526,7 +547,7 @@ SwVirtFlyDrawObj* SwFlyDrawContact::CreateNewRef(SwFlyFrame* pFly, SwFlyFrameFor // #i27030# - insert new <SwVirtFlyDrawObj> instance // into drawing page with correct order number else - rIDDMA.GetDrawModel()->GetPage(0)->InsertObject(pDrawObj, pContact->GetOrdNumForNewRef(pFly)); + rIDDMA.GetDrawModel()->GetPage(0)->InsertObject(pDrawObj, pContact->GetOrdNumForNewRef(pFly, rAnchorFrame)); // #i38889# - assure, that new <SwVirtFlyDrawObj> instance // is in a visible layer. pContact->MoveObjToVisibleLayer(pDrawObj); @@ -804,7 +825,7 @@ SwFrame* SwDrawContact::GetAnchorFrame(SdrObject const *const pDrawObj) /** add a 'virtual' drawing object to drawing page. */ -SwDrawVirtObj* SwDrawContact::AddVirtObj() +SwDrawVirtObj* SwDrawContact::AddVirtObj(SwFrame const& rAnchorFrame) { maDrawVirtObjs.push_back( SwDrawVirtObjPtr( @@ -812,7 +833,7 @@ SwDrawVirtObj* SwDrawContact::AddVirtObj() GetMaster()->getSdrModelFromSdrObject(), *GetMaster(), *this))); - maDrawVirtObjs.back()->AddToDrawingPage(); + maDrawVirtObjs.back()->AddToDrawingPage(rAnchorFrame); return maDrawVirtObjs.back().get(); } @@ -1871,7 +1892,7 @@ void SwDrawContact::ConnectToLayout( const SwFormatAnchor* pAnch ) else { // append 'virtual' drawing object - SwDrawVirtObj* pDrawVirtObj = AddVirtObj(); + SwDrawVirtObj* pDrawVirtObj = AddVirtObj(*pFrame); if ( pAnch->GetAnchorId() == RndStdIds::FLY_AS_CHAR ) { ClrContourCache( pDrawVirtObj ); @@ -2220,30 +2241,56 @@ void SwDrawVirtObj::RemoveFromWriterLayout() } } -void SwDrawVirtObj::AddToDrawingPage() +void SwDrawVirtObj::AddToDrawingPage(SwFrame const& rAnchorFrame) { // determine 'master' SdrObject* pOrgMasterSdrObj = mrDrawContact.GetMaster(); // insert 'virtual' drawing object into page, set layer and user call. SdrPage* pDrawPg; + // default: insert before master object + auto NOTM_nOrdNum(GetReferencedObj().GetOrdNum()); + + // maintain invariant that a shape's textbox immediately follows the shape + // also for the multiple SdrDrawVirtObj created for shapes in header/footer + if (SwFrameFormat const*const pFlyFormat = + SwTextBoxHelper::getOtherTextBoxFormat(mrDrawContact.GetFormat(), RES_DRAWFRMFMT)) + { + // this is for the case when the flyframe SdrVirtObj is created before the draw one + if (SwSortedObjs const*const pObjs = rAnchorFrame.GetDrawObjs()) + { + for (SwAnchoredObject const*const pAnchoredObj : *pObjs) + { + if (&pAnchoredObj->GetFrameFormat() == pFlyFormat) + { + assert(dynamic_cast<SwFlyFrame const*>(pAnchoredObj)); + NOTM_nOrdNum = pAnchoredObj->GetDrawObj()->GetOrdNum(); + // the master SdrObj should have the highest index + assert(NOTM_nOrdNum < GetReferencedObj().GetOrdNum()); + break; + } + } + } + // this happens on initial insertion, the draw object is created first + SAL_INFO_IF(GetReferencedObj().GetOrdNum() == NOTM_nOrdNum, "sw", "AddToDrawingPage: cannot find SdrObject for text box's shape"); + } + // #i27030# - apply order number of referenced object if ( nullptr != ( pDrawPg = pOrgMasterSdrObj->getSdrPageFromSdrObject() ) ) { // #i27030# - apply order number of referenced object - pDrawPg->InsertObject( this, GetReferencedObj().GetOrdNum() ); + pDrawPg->InsertObject(this, NOTM_nOrdNum); } else { pDrawPg = getSdrPageFromSdrObject(); if ( pDrawPg ) { - pDrawPg->SetObjectOrdNum( GetOrdNumDirect(), - GetReferencedObj().GetOrdNum() ); + pDrawPg->SetObjectOrdNum(GetOrdNumDirect(), NOTM_nOrdNum); } else { - SetOrdNum( GetReferencedObj().GetOrdNum() ); + SetOrdNum(NOTM_nOrdNum); } } SetUserCall( &mrDrawContact ); diff --git a/sw/source/core/draw/dview.cxx b/sw/source/core/draw/dview.cxx index c1a7b6a8cbbc..b20ae1031382 100644 --- a/sw/source/core/draw/dview.cxx +++ b/sw/source/core/draw/dview.cxx @@ -231,6 +231,11 @@ void SwDrawView::AddCustomHdl() // #i28701# - use last character rectangle saved at object // in order to avoid a format of the anchor frame SwAnchoredObject* pAnchoredObj = ::GetUserCall( pObj )->GetAnchoredObj( pObj ); + + // Invalidate/recalc LastCharRect which can contain invalid frame offset because + // of later frame changes + pAnchoredObj->CheckCharRectAndTopOfLine(false); + SwRect aAutoPos = pAnchoredObj->GetLastCharRect(); if ( aAutoPos.Height() ) { 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 a3687b5e308c..80b91de1d649 100644 --- a/sw/source/core/edit/autofmt.cxx +++ b/sw/source/core/edit/autofmt.cxx @@ -571,29 +571,29 @@ bool SwAutoFormat::DoUnderline() editeng::SvxBorderLine aLine; switch( eState ) { - case 1: // single, 0.05 pt + case 1: // single, hairline aLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID); - aLine.SetWidth( DEF_LINE_WIDTH_0 ); + aLine.SetWidth( SvxBorderLineWidth::Hairline ); break; - case 2: // single, 1.0 pt + case 2: // single, thin aLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID); - aLine.SetWidth( DEF_LINE_WIDTH_1 ); + aLine.SetWidth( SvxBorderLineWidth::Thin ); break; - case 3: // double, 1.0 pt + case 3: // double, thin aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE); - aLine.SetWidth( DEF_LINE_WIDTH_1 ); + aLine.SetWidth( SvxBorderLineWidth::Thin ); break; - case 4: // double (thick/thin), 4.0 pt + case 4: // double, thick/thin aLine.SetBorderLineStyle(SvxBorderLineStyle::THICKTHIN_SMALLGAP); - aLine.SetWidth( DEF_LINE_WIDTH_3 ); + aLine.SetWidth( SvxBorderLineWidth::Thick ); break; - case 5: // double (thin/thick), 4.0 pt + case 5: // double, thin/thick aLine.SetBorderLineStyle(SvxBorderLineStyle::THINTHICK_SMALLGAP); - aLine.SetWidth( DEF_LINE_WIDTH_3 ); + aLine.SetWidth( SvxBorderLineWidth::Thick ); break; - case 6: // double, 2.5 pt + case 6: // double, medium aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE); - aLine.SetWidth( DEF_LINE_WIDTH_2 ); + aLine.SetWidth( SvxBorderLineWidth::Medium ); break; } SfxItemSet aSet(m_pDoc->GetAttrPool(), @@ -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, @@ -1904,11 +1904,14 @@ void SwAutoFormat::BuildHeadLine( sal_uInt16 nLvl ) JoinPrevPara(); DeleteLeadingTrailingBlanks( true, false ); - const SwTextFrame *const pNextFrame = GetNextNode(false); - (void)DeleteJoinCurNextPara(pNextFrame, true); - + const SwTextFrame* pNextFrame = GetNextNode(false); + if (pNextFrame->GetNext()) + { + (void)DeleteJoinCurNextPara(pNextFrame, true); + pNextFrame = GetNextNode(false); + } m_aDelPam.DeleteMark(); - m_aDelPam.GetPoint()->nNode = *GetNextNode(false)->GetTextNodeForParaProps(); + m_aDelPam.GetPoint()->nNode = *pNextFrame->GetTextNodeForParaProps(); m_aDelPam.GetPoint()->nContent.Assign( m_aDelPam.GetContentNode(), 0 ); m_pDoc->SetTextFormatColl( m_aDelPam, &rNxtColl ); } 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..37c19e60a91e 100644 --- a/sw/source/core/edit/eddel.cxx +++ b/sw/source/core/edit/eddel.cxx @@ -38,7 +38,7 @@ #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(); // only for selections @@ -121,7 +121,8 @@ void SwEditShell::DeleteSel( SwPaM& rPam, bool* pUndo ) pPam = pNewPam.get(); } // delete everything - GetDoc()->getIDocumentContentOperations().DeleteAndJoin(*pPam); + GetDoc()->getIDocumentContentOperations().DeleteAndJoin(*pPam, + isArtificialSelection ? SwDeleteFlags::ArtificialSelection : SwDeleteFlags::Default); SaveTableBoxContent( pPam->GetPoint() ); } @@ -129,7 +130,7 @@ void SwEditShell::DeleteSel( SwPaM& rPam, bool* pUndo ) rPam.DeleteMark(); } -bool SwEditShell::Delete() +bool SwEditShell::Delete(bool const isArtificialSelection) { SET_CURR_SHELL( this ); bool bRet = false; @@ -148,7 +149,7 @@ bool SwEditShell::Delete() for(SwPaM& rPaM : GetCursor()->GetRingContainer()) { - DeleteSel( rPaM, &bUndo ); + DeleteSel(rPaM, isArtificialSelection, &bUndo); } // If undo container then close here diff --git a/sw/source/core/edit/edfld.cxx b/sw/source/core/edit/edfld.cxx index 7525a9af80af..75364a42bd1d 100644 --- a/sw/source/core/edit/edfld.cxx +++ b/sw/source/core/edit/edfld.cxx @@ -149,7 +149,7 @@ void SwEditShell::FieldToText( SwFieldType const * pType ) } /// add a field at the cursor position -void SwEditShell::Insert2(SwField const & rField, const bool bForceExpandHints) +bool SwEditShell::InsertField(SwField const & rField, const bool bForceExpandHints) { SET_CURR_SHELL( this ); StartAllAction(); @@ -159,13 +159,15 @@ void SwEditShell::Insert2(SwField const & rField, const bool bForceExpandHints) ? SetAttrMode::FORCEHINTEXPAND : SetAttrMode::DEFAULT; + bool bSuccess(false); for(const SwPaM& rPaM : GetCursor()->GetRingContainer()) // for each PaM { - const bool bSuccess(GetDoc()->getIDocumentContentOperations().InsertPoolItem(rPaM, aField, nInsertFlags)); + bSuccess |= GetDoc()->getIDocumentContentOperations().InsertPoolItem(rPaM, aField, nInsertFlags); OSL_ENSURE( bSuccess, "Doc->Insert(Field) failed"); } EndAllAction(); + return bSuccess; } /// Are the PaMs positioned on fields? @@ -223,7 +225,7 @@ void SwEditShell::UpdateOneField(SwField &rField) if ( !pCursor->IsMultiSelection() && !pCursor->HasMark()) { - pTextField = GetTextFieldAtPos( pCursor->Start(), true ); + pTextField = GetTextFieldAtPos(pCursor->Start(), ::sw::GetTextAttrMode::Default); if (!pTextField) // #i30221# pTextField = lcl_FindInputField( GetDoc(), rField); @@ -268,7 +270,8 @@ void SwEditShell::UpdateOneField(SwField &rField) if( aPam.Start()->nContent != pCurStt->nContent ) bOkay = false; - if( nullptr != (pTextField = GetTextFieldAtPos( pCurStt, true )) ) + pTextField = GetTextFieldAtPos(pCurStt, ::sw::GetTextAttrMode::Default); + if( nullptr != pTextField ) { pFormatField = const_cast<SwFormatField*>(&pTextField->GetFormatField()); SwField *pCurField = pFormatField->GetField(); 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/editsh.cxx b/sw/source/core/edit/editsh.cxx index 8f84ce42ed75..699997003daf 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 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/expfld.cxx b/sw/source/core/fields/expfld.cxx index 673e257e0554..c702e17d1cd5 100644 --- a/sw/source/core/fields/expfld.cxx +++ b/sw/source/core/fields/expfld.cxx @@ -1313,6 +1313,7 @@ std::unique_ptr<SwField> SwInputField::Copy() const pField->SetHelp( maHelp ); pField->SetToolTip( maToolTip ); + pField->maGrabBag = maGrabBag; pField->SetAutomaticLanguage(IsAutomaticLanguage()); return std::unique_ptr<SwField>(pField.release()); @@ -1359,6 +1360,9 @@ bool SwInputField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const case FIELD_PROP_PAR4: rAny <<= maToolTip; break; + case FIELD_PROP_GRABBAG: + rAny <<= maGrabBag; + break; default: assert(false); } @@ -1381,6 +1385,9 @@ bool SwInputField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) case FIELD_PROP_PAR4: rAny >>= maToolTip; break; + case FIELD_PROP_GRABBAG: + rAny >>= maGrabBag; + break; default: assert(false); } 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..8589b8eed579 100644 --- a/sw/source/core/frmedt/fecopy.cxx +++ b/sw/source/core/frmedt/fecopy.cxx @@ -1021,7 +1021,7 @@ bool SwFEShell::Paste( SwDoc* pClpDoc, bool bNestedTable ) { if( bDelTable && IsTableMode() ) { - SwEditShell::Delete(); + SwEditShell::Delete(false); bDelTable = 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 edf8121836b6..7cb52c1a7c1e 100644 --- a/sw/source/core/inc/MarkManager.hxx +++ b/sw/source/core/inc/MarkManager.hxx @@ -67,7 +67,7 @@ namespace sw { // deleters virtual std::unique_ptr<ILazyDeleter> - deleteMark(const const_iterator_t& ppMark) override; + deleteMark(const const_iterator_t& ppMark, bool isMoveNodes) override; virtual void deleteMark(const ::sw::mark::IMark* const pMark) override; virtual void clearAllMarks() 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/bookmrk.hxx b/sw/source/core/inc/bookmrk.hxx index 3960ca4b3d8b..fe5bff942568 100644 --- a/sw/source/core/inc/bookmrk.hxx +++ b/sw/source/core/inc/bookmrk.hxx @@ -90,6 +90,8 @@ namespace sw { virtual void ClearOtherMarkPos() { m_pPos2.reset(); } + virtual auto InvalidateFrames() -> void; + virtual OUString ToString( ) const override; virtual void dumpAsXml(xmlTextWriterPtr pWriter) const override; @@ -170,6 +172,8 @@ namespace sw { virtual void DeregisterFromDoc(SwDoc* const io_pDoc) override; + virtual auto InvalidateFrames() -> void override; + virtual const OUString& GetShortName() const override { return m_sShortName; } virtual const vcl::KeyCode& GetKeyCode() const override @@ -182,10 +186,8 @@ namespace sw { { return m_bHidden; } virtual const OUString& GetHideCondition() const override { return m_sHideCondition; } - virtual void Hide(bool rHide) override - { m_bHidden = rHide; } - virtual void SetHideCondition(const OUString& rHideCondition) override - { m_sHideCondition = rHideCondition; } + virtual void Hide(bool rHide) override; + virtual void SetHideCondition(const OUString& rHideCondition) override; // ::sfx2::Metadatable virtual ::sfx2::IXmlIdRegistry& GetRegistry() override; diff --git a/sw/source/core/inc/docfld.hxx b/sw/source/core/inc/docfld.hxx index 36cf3d86eba0..2ab96b1639c1 100644 --- a/sw/source/core/inc/docfld.hxx +++ b/sw/source/core/inc/docfld.hxx @@ -23,6 +23,7 @@ #include <calc.hxx> #include <doc.hxx> #include <IDocumentTimerAccess.hxx> +#include <IMark.hxx> #include <o3tl/sorted_vector.hxx> #include <memory> @@ -53,10 +54,11 @@ class SetGetExpField const SwTableBox* pTBox; const SwTextINetFormat* pTextINet; const SwFlyFrameFormat* pFlyFormat; + ::sw::mark::IBookmark const* pBookmark; } m_CNTNT; enum SetGetExpFieldType { - TEXTFIELD, TEXTTOXMARK, SECTIONNODE, CRSRPOS, TABLEBOX, + TEXTFIELD, TEXTTOXMARK, SECTIONNODE, BOOKMARK, CRSRPOS, TABLEBOX, TEXTINET, FLYFRAME } m_eSetGetExpFieldType; @@ -69,6 +71,9 @@ public: SetGetExpField( const SwSectionNode& rSectNode, const SwPosition* pPos = nullptr ); + SetGetExpField( ::sw::mark::IBookmark const& rBookmark, + SwPosition const* pPos = nullptr ); + SetGetExpField( const SwTableBox& rTableBox ); SetGetExpField( const SwNodeIndex& rNdIdx, const SwTextTOXMark& rTOX ); @@ -84,6 +89,8 @@ public: { return TEXTFIELD == m_eSetGetExpFieldType ? m_CNTNT.pTextField : nullptr; } const SwSection* GetSection() const { return SECTIONNODE == m_eSetGetExpFieldType ? m_CNTNT.pSection : nullptr; } + ::sw::mark::IBookmark const* GetBookmark() const + { return BOOKMARK == m_eSetGetExpFieldType ? m_CNTNT.pBookmark : nullptr; } const SwTextINetFormat* GetINetFormat() const { return TEXTINET == m_eSetGetExpFieldType ? m_CNTNT.pTextINet : nullptr; } const SwFlyFrameFormat* GetFlyFormat() const @@ -142,7 +149,8 @@ class SwDocUpdateField void MakeFieldList_( SwDoc& pDoc, int eGetMode ); void GetBodyNode( const SwTextField& , SwFieldIds nFieldWhich ); - void GetBodyNode( const SwSectionNode&); + template<typename T> + void GetBodyNodeGeneric(SwNode const& rNode, T const&); public: SwDocUpdateField(SwDoc& rDocument); diff --git a/sw/source/core/inc/flyfrm.hxx b/sw/source/core/inc/flyfrm.hxx index 18f263cfc1db..29a196944fcb 100644 --- a/sw/source/core/inc/flyfrm.hxx +++ b/sw/source/core/inc/flyfrm.hxx @@ -64,7 +64,7 @@ class SW_DLLPUBLIC SwFlyFrame : public SwLayoutFrame, public SwAnchoredObject friend void Notify( SwFlyFrame *, SwPageFrame *pOld, const SwRect &rOld, const SwRect* pOldPrt ); - void InitDrawObj(); // these to methods are called in the + void InitDrawObj(SwFrame const&); // these to methods are called in the void FinitDrawObj(); // constructors void UpdateAttr_( const SfxPoolItem*, const SfxPoolItem*, sal_uInt8 &, @@ -134,6 +134,7 @@ protected: page frame */ virtual void RegisterAtCorrectPage() override; + virtual void RegisterAtPage(SwPageFrame &) override; virtual bool SetObjTop_( const SwTwips _nTop ) override; virtual bool SetObjLeft_( const SwTwips _nLeft ) override; diff --git a/sw/source/core/inc/flyfrms.hxx b/sw/source/core/inc/flyfrms.hxx index 4c6940c28edc..cb94b7cbd259 100644 --- a/sw/source/core/inc/flyfrms.hxx +++ b/sw/source/core/inc/flyfrms.hxx @@ -151,6 +151,7 @@ public: SwFlyLayFrame( SwFlyFrameFormat*, SwFrame*, SwFrame *pAnchor ); + virtual void RegisterAtPage(SwPageFrame &) override; protected: virtual void Modify( const SfxPoolItem*, const SfxPoolItem* ) override; }; @@ -170,6 +171,7 @@ protected: #i28701# */ virtual void RegisterAtCorrectPage() override; + virtual void RegisterAtPage(SwPageFrame &) override; virtual void Modify( const SfxPoolItem*, const SfxPoolItem* ) override; public: @@ -227,6 +229,7 @@ public: //see layact.cxx void AddRefOfst( long nOfst ) { aRef.AdjustY( nOfst ); } + void AddRefOfst(Point const& rOfst) { aRef += rOfst; } // #i26791# virtual void MakeObjPos() override; diff --git a/sw/source/core/inc/frame.hxx b/sw/source/core/inc/frame.hxx index ae5e8378ee4b..289a45fc922f 100644 --- a/sw/source/core/inc/frame.hxx +++ b/sw/source/core/inc/frame.hxx @@ -424,7 +424,7 @@ protected: bool mbColLocked : 1; // lock Grow/Shrink for column-wise section // or fly frames, will be set in Format bool m_isInDestroy : 1; - bool mbForbidDelete : 1; + int mnForbidDelete; void ColLock() { mbColLocked = true; } void ColUnlock() { mbColLocked = false; } @@ -860,7 +860,7 @@ public: bool IsProtected() const; bool IsColLocked() const { return mbColLocked; } - virtual bool IsDeleteForbidden() const { return mbForbidDelete; } + virtual bool IsDeleteForbidden() const { return mnForbidDelete > 0; } /// this is the only way to delete a SwFrame instance static void DestroyFrame(SwFrame *const pFrame); @@ -900,8 +900,8 @@ public: void RegisterToFormat( SwFormat& rFormat ); void ValidateThisAndAllLowers( const sal_uInt16 nStage ); - void ForbidDelete() { mbForbidDelete = true; } - void AllowDelete() { mbForbidDelete = false; } + void ForbidDelete() { ++mnForbidDelete; } + void AllowDelete() { assert(mnForbidDelete > 0); --mnForbidDelete; } drawinglayer::attribute::SdrAllFillAttributesHelperPtr getSdrAllFillAttributesHelper() const; bool supportsFullDrawingLayerFillAttributeSet() const; @@ -1238,8 +1238,7 @@ public: //it in e.g. SwSectionFrame::MergeNext etc because we will need it //again after the SwFrameDeleteGuard dtor explicit SwFrameDeleteGuard(SwFrame* pFrame) - : m_pForbidFrame((pFrame && !pFrame->IsDeleteForbidden()) ? - pFrame : nullptr) + : m_pForbidFrame(pFrame) { if (m_pForbidFrame) m_pForbidFrame->ForbidDelete(); diff --git a/sw/source/core/inc/layact.hxx b/sw/source/core/inc/layact.hxx index 990c0e4b88f0..75a0523fda7f 100644 --- a/sw/source/core/inc/layact.hxx +++ b/sw/source/core/inc/layact.hxx @@ -68,6 +68,9 @@ class SwLayAction std::unique_ptr<SwWait> m_pWait; + std::vector<SwFrame*> m_aFrameStack; + std::vector<std::unique_ptr<SwFrameDeleteGuard>> m_aFrameDeleteGuards; + // If a paragraph (or anything else) moved more than one page when // formatting, it adds its new page number here. // The InternalAction can then take the appropriate steps. @@ -111,7 +114,7 @@ class SwLayAction bool FormatLayout( OutputDevice* pRenderContext, SwLayoutFrame *, bool bAddRect = true ); bool FormatLayoutTab( SwTabFrame *, bool bAddRect ); - bool FormatContent( const SwPageFrame* pPage ); + bool FormatContent(SwPageFrame * pPage); void FormatContent_( const SwContentFrame* pContent, const SwPageFrame* pPage ); bool IsShortCut( SwPageFrame *& ); @@ -124,6 +127,9 @@ class SwLayAction bool RemoveEmptyBrowserPages(); + void PushFormatLayout(SwFrame* pLow); + void PopFormatLayout(); + public: SwLayAction(SwRootFrame *pRt, SwViewShellImp *pImp, TaskStopwatch* pWatch = nullptr); ~SwLayAction(); @@ -148,7 +154,7 @@ public: void SetReschedule ( bool bNew ) { m_bReschedule = bNew; } void SetWaitAllowed ( bool bNew ) { m_bWaitAllowed = bNew; } - void SetAgain() { m_bAgain = true; } + void SetAgain(bool bAgain); void SetUpdateExpFields() {m_bUpdateExpFields = true; } inline void SetCheckPageNum( sal_uInt16 nNew ); diff --git a/sw/source/core/inc/layfrm.hxx b/sw/source/core/inc/layfrm.hxx index f7d90f00d8f6..835b20c0cfd6 100644 --- a/sw/source/core/inc/layfrm.hxx +++ b/sw/source/core/inc/layfrm.hxx @@ -100,6 +100,7 @@ public: SwPrintData const*const pPrintData = nullptr ) const override; const SwFrame *Lower() const { return m_pLower; } SwFrame *Lower() { return m_pLower; } + bool ContainsDeleteForbiddenLayFrame() const; const SwContentFrame *ContainsContent() const; inline SwContentFrame *ContainsContent(); const SwCellFrame *FirstCell() const; diff --git a/sw/source/core/inc/mvsave.hxx b/sw/source/core/inc/mvsave.hxx index c472b6f7bc1a..5b84b5e10819 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; @@ -116,10 +117,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/pagefrm.hxx b/sw/source/core/inc/pagefrm.hxx index 95d69bd699ee..878fae3a0520 100644 --- a/sw/source/core/inc/pagefrm.hxx +++ b/sw/source/core/inc/pagefrm.hxx @@ -435,6 +435,9 @@ SwTextGridItem const* GetGridItem(SwPageFrame const*const); sal_uInt16 GetGridWidth(SwTextGridItem const&, SwDoc const&); +namespace sw { bool IsPageFrameEmpty(SwPageFrame const& rPage); } + + #endif // INCLUDED_SW_SOURCE_CORE_INC_PAGEFRM_HXX /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 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/tabfrm.hxx b/sw/source/core/inc/tabfrm.hxx index 95da935cd079..6dfa6c743a13 100644 --- a/sw/source/core/inc/tabfrm.hxx +++ b/sw/source/core/inc/tabfrm.hxx @@ -43,6 +43,7 @@ class SwTabFrame: public SwLayoutFrame, public SwFlowFrame bool m_bCalcLowers :1; /// For stability of the content in MakeAll bool m_bLowersFormatted :1; /// Communication between MakeAll and Layact bool m_bLockBackMove :1; /// The Master took care of the BackMove test + bool m_bWantBackMove :1; /// Table wants to move back but was locked bool m_bResizeHTMLTable :1; /// Call the Resize of the HTMLTableLayout in the MakeAll /// This is an optimization, so that we don't have to call /// it in ContentFrame::Grow; there it might be called for diff --git a/sw/source/core/inc/unofldmid.h b/sw/source/core/inc/unofldmid.h index 2bb13a66faa3..43e30058d470 100644 --- a/sw/source/core/inc/unofldmid.h +++ b/sw/source/core/inc/unofldmid.h @@ -41,6 +41,7 @@ #define FIELD_PROP_BOOL4 28 #define FIELD_PROP_STRINGS 29 #define FIELD_PROP_PAR5 30 +#define FIELD_PROP_GRABBAG 31 #define FIELD_PROP_IS_FIELD_USED 32 #define FIELD_PROP_IS_FIELD_DISPLAYED 33 diff --git a/sw/source/core/layout/anchoreddrawobject.cxx b/sw/source/core/layout/anchoreddrawobject.cxx index 079468fdf062..438759f765b2 100644 --- a/sw/source/core/layout/anchoreddrawobject.cxx +++ b/sw/source/core/layout/anchoreddrawobject.cxx @@ -840,10 +840,18 @@ void SwAnchoredDrawObject::RegisterAtCorrectPage() } if ( pPageFrame && GetPageFrame() != pPageFrame ) { - if ( GetPageFrame() ) - GetPageFrame()->RemoveDrawObjFromPage( *this ); - pPageFrame->AppendDrawObjToPage( *this ); + RegisterAtPage(*pPageFrame); } } +void SwAnchoredDrawObject::RegisterAtPage(SwPageFrame & rPageFrame) +{ + assert(GetPageFrame() != &rPageFrame); + if (GetPageFrame()) + { + GetPageFrame()->RemoveDrawObjFromPage( *this ); + } + rPageFrame.AppendDrawObjToPage( *this ); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 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/findfrm.cxx b/sw/source/core/layout/findfrm.cxx index a35af84d54a1..632e136fdd10 100644 --- a/sw/source/core/layout/findfrm.cxx +++ b/sw/source/core/layout/findfrm.cxx @@ -161,6 +161,27 @@ const SwFrame *SwLayoutFrame::ContainsAny( const bool _bInvestigateFootnoteForSe return nullptr; } +bool SwLayoutFrame::ContainsDeleteForbiddenLayFrame() const +{ + if (IsDeleteForbidden()) + { + return true; + } + for (SwFrame const* pFrame = Lower(); pFrame; pFrame = pFrame->GetNext()) + { + if (!pFrame->IsLayoutFrame()) + { + continue; + } + SwLayoutFrame const*const pLay(static_cast<SwLayoutFrame const*>(pFrame)); + if (pLay->ContainsDeleteForbiddenLayFrame()) + { + return true; + } + } + return false; +} + const SwFrame* SwFrame::GetLower() const { return IsLayoutFrame() ? static_cast<const SwLayoutFrame*>(this)->Lower() : nullptr; diff --git a/sw/source/core/layout/flowfrm.cxx b/sw/source/core/layout/flowfrm.cxx index d416b637c02f..2aaa2e2360fb 100644 --- a/sw/source/core/layout/flowfrm.cxx +++ b/sw/source/core/layout/flowfrm.cxx @@ -2034,7 +2034,7 @@ bool SwFlowFrame::MoveFwd( bool bMakePage, bool bPageBreak, bool bMoveAlways ) // #i106452# // check page description not only in situation with sections. if ( !bSamePage && - ( m_rThis.GetPageDescItem().GetPageDesc() || + ((!IsFollow() && m_rThis.GetPageDescItem().GetPageDesc()) || pOldPage->GetPageDesc()->GetFollow() != pNewPage->GetPageDesc() ) ) { SwFrame::CheckPageDescs( pNewPage, false ); diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx index 190e799dbc56..2b6973d14e97 100644 --- a/sw/source/core/layout/fly.cxx +++ b/sw/source/core/layout/fly.cxx @@ -165,7 +165,7 @@ SwFlyFrame::SwFlyFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch // First the Init, then the Content: // This is due to the fact that the Content may have Objects/Frames, // which are then registered - InitDrawObj(); + InitDrawObj(*pAnch); Chain( pAnch ); @@ -361,10 +361,10 @@ void SwFlyFrame::DeleteCnt() InvalidatePage(); } -void SwFlyFrame::InitDrawObj() +void SwFlyFrame::InitDrawObj(SwFrame const& rAnchorFrame) { // OD 2004-03-22 #i26791# - SetDrawObj(*SwFlyDrawContact::CreateNewRef(this, GetFormat())); + SetDrawObj(*SwFlyDrawContact::CreateNewRef(this, GetFormat(), rAnchorFrame)); // Set the right Layer // OD 2004-01-19 #110582# @@ -1469,6 +1469,7 @@ void CalcContent( SwLayoutFrame *pLay, bool bNoColl ) do { pLast = pFrame; + bool const wasFrameLowerOfLay(pLay->IsAnLower(pFrame)); if( pFrame->IsVertical() ? ( pFrame->GetUpper()->getFramePrintArea().Height() != pFrame->getFrameArea().Height() ) : ( pFrame->GetUpper()->getFramePrintArea().Width() != pFrame->getFrameArea().Width() ) ) @@ -1528,11 +1529,13 @@ void CalcContent( SwLayoutFrame *pLay, bool bNoColl ) { bool bAgain = false; bool bRestartLayoutProcess = false; - SwPageFrame* pPageFrame = pFrame->FindPageFrame(); size_t nCnt = pFrame->GetDrawObjs()->size(); size_t i = 0; while ( i < nCnt ) { + // pFrame can move to a different page in FormatObj() + SwPageFrame *const pPageFrame = pFrame->FindPageFrame(); + // #i28701# SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i]; assert(pAnchoredObj); @@ -1610,7 +1613,10 @@ void CalcContent( SwLayoutFrame *pLay, bool bNoColl ) // #i28701# - restart layout process, if // requested by floating screen object formatting - if ( bRestartLayoutProcess ) + if (bRestartLayoutProcess + // tdf#142080 if it was aleady on next page, and still is, + // ignore restart, as restart could cause infinite loop + && (wasFrameLowerOfLay || pLay->IsAnLower(pFrame))) { pFrame = pLay->ContainsAny(); pAgainObj1 = nullptr; @@ -1653,8 +1659,17 @@ void CalcContent( SwLayoutFrame *pLay, bool bNoColl ) } if ( pFrame->IsTabFrame() ) { - if ( static_cast<SwTabFrame*>(pFrame)->IsFollow() ) + if (static_cast<SwTabFrame*>(pFrame)->m_bLockBackMove) + { + assert(static_cast<SwTabFrame*>(pFrame)->IsFollow()); static_cast<SwTabFrame*>(pFrame)->m_bLockBackMove = false; + // tdf#150606 encourage it to move back in FormatLayout() + if (static_cast<SwTabFrame*>(pFrame)->m_bWantBackMove) + { + static_cast<SwTabFrame*>(pFrame)->m_bWantBackMove = false; + pFrame->InvalidatePos(); + } + } } pFrame = bPrevInvalid ? pTmpPrev : pFrame->FindNext(); @@ -1674,10 +1689,10 @@ void CalcContent( SwLayoutFrame *pLay, bool bNoColl ) pFrame->InvalidatePos_(); } } - // Stay in the pLay - // Except for SectionFrames with Follow: the first ContentFrame of the Follow - // will be formatted, so that it gets a chance to load in the pLay. - // As long as these Frames are loading in pLay, we continue + // Stay in the pLay. + // Except for SectionFrames with Follow: the first ContentFrame of the + // Follow will be formatted, so that it gets a chance to move back + // into the pLay. Continue as long as these Frames land in pLay. } while ( pFrame && ( pLay->IsAnLower( pFrame ) || ( pSect && @@ -2839,6 +2854,11 @@ void SwFlyFrame::RegisterAtCorrectPage() // default behaviour is to do nothing. } +void SwFlyFrame::RegisterAtPage(SwPageFrame &) +{ + // default behaviour is to do nothing. +} + /** method to determine, if a <MakeAll()> on the Writer fly frame is possible OD 2004-05-11 #i28701# diff --git a/sw/source/core/layout/flycnt.cxx b/sw/source/core/layout/flycnt.cxx index f3c442ac8cfa..06baefd15fa7 100644 --- a/sw/source/core/layout/flycnt.cxx +++ b/sw/source/core/layout/flycnt.cxx @@ -421,11 +421,17 @@ void SwFlyAtContentFrame::MakeAll(vcl::RenderContext* pRenderContext) // <SwObjectFormatterTextFrame::CheckMovedFwdCondition(..)> sal_uInt32 nToPageNum( 0 ); bool bDummy( false ); + bool bPageHasFlysAnchoredBelowThis(false); if ( SwObjectFormatterTextFrame::CheckMovedFwdCondition( - *this, GetPageFrame()->GetPhyPageNum(), - bAnchoredAtMaster, nToPageNum, bDummy ) ) +// TODO: what if this fly moved bc it's in table? does sth prevent that? + *this, *GetPageFrame(), + bAnchoredAtMaster, nToPageNum, bDummy, + bPageHasFlysAnchoredBelowThis) ) { - bConsiderWrapInfluenceDueToMovedFwdAnchor = true; + if (!bPageHasFlysAnchoredBelowThis) + { + bConsiderWrapInfluenceDueToMovedFwdAnchor = true; + } // mark anchor text frame // directly, that it is moved forward by object positioning. SwTextFrame* pAnchorTextFrame( static_cast<SwTextFrame*>(AnchorFrame()) ); @@ -436,14 +442,22 @@ void SwFlyAtContentFrame::MakeAll(vcl::RenderContext* pRenderContext) rDoc, *pAnchorTextFrame, nAnchorFrameToPageNum ) ) { if ( nAnchorFrameToPageNum < nToPageNum ) - SwLayouter::RemoveMovedFwdFrame( rDoc, *pAnchorTextFrame ); + { + if (!bPageHasFlysAnchoredBelowThis) + { + SwLayouter::RemoveMovedFwdFrame(rDoc, *pAnchorTextFrame); + } + } else bInsert = false; } if ( bInsert ) { - SwLayouter::InsertMovedFwdFrame( rDoc, *pAnchorTextFrame, - nToPageNum ); + if (!bPageHasFlysAnchoredBelowThis) + { + SwLayouter::InsertMovedFwdFrame(rDoc, *pAnchorTextFrame, + nToPageNum); + } } } } @@ -1333,7 +1347,7 @@ void SwFlyAtContentFrame::SetAbsPos( const Point &rNew ) { const SwTextAttr *const pTextInputField = pos.nNode.GetNode().GetTextNode()->GetTextAttrAt( - pos.nContent.GetIndex(), RES_TXTATR_INPUTFIELD, SwTextNode::PARENT ); + pos.nContent.GetIndex(), RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent); if (pTextInputField != nullptr) { pos.nContent = pTextInputField->GetStart(); @@ -1396,10 +1410,20 @@ void SwFlyAtContentFrame::RegisterAtCorrectPage() } if ( pPageFrame && GetPageFrame() != pPageFrame ) { - if ( GetPageFrame() ) - GetPageFrame()->MoveFly( this, pPageFrame ); - else - pPageFrame->AppendFlyToPage( this ); + RegisterAtPage(*pPageFrame); + } +} + +void SwFlyAtContentFrame::RegisterAtPage(SwPageFrame & rPageFrame) +{ + assert(GetPageFrame() != &rPageFrame); + if (GetPageFrame()) + { + GetPageFrame()->MoveFly( this, &rPageFrame ); + } + else + { + rPageFrame.AppendFlyToPage( this ); } } diff --git a/sw/source/core/layout/flylay.cxx b/sw/source/core/layout/flylay.cxx index 4a7030ea241e..c915ee82d95a 100644 --- a/sw/source/core/layout/flylay.cxx +++ b/sw/source/core/layout/flylay.cxx @@ -723,6 +723,19 @@ SwFlyLayFrame::SwFlyLayFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame m_bLayout = true; } +void SwFlyLayFrame::RegisterAtPage(SwPageFrame & rPageFrame) +{ + assert(GetPageFrame() != &rPageFrame); + if (GetPageFrame()) + { + GetPageFrame()->MoveFly( this, &rPageFrame ); + } + else + { + rPageFrame.AppendFlyToPage( this ); + } +} + // #i28701# void SwFlyLayFrame::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew ) diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx index a861dd60013b..f702815b4010 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> @@ -1018,7 +1019,7 @@ void AppendObj(SwFrame *const pFrame, SwPageFrame *const pPage, SwFrameFormat *c pNew->GetAnchorFrame() != pFrame && !pNew->GetDrawObjectByAnchorFrame( *pFrame ) ) { - SwDrawVirtObj* pDrawVirtObj = pNew->AddVirtObj(); + SwDrawVirtObj* pDrawVirtObj = pNew->AddVirtObj(*pFrame); pFrame->AppendDrawObj( *(pNew->GetAnchoredObj( pDrawVirtObj )) ); pDrawVirtObj->ActionChanged(); @@ -1067,6 +1068,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 +1168,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)) @@ -1719,7 +1725,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 @@ -1941,8 +1950,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; diff --git a/sw/source/core/layout/ftnfrm.cxx b/sw/source/core/layout/ftnfrm.cxx index 9573944b71d1..964af2008868 100644 --- a/sw/source/core/layout/ftnfrm.cxx +++ b/sw/source/core/layout/ftnfrm.cxx @@ -572,10 +572,20 @@ void SwFootnoteFrame::Paste( SwFrame* pParent, SwFrame* pSibling ) if( aRectFnSet.GetWidth(getFrameArea())!=aRectFnSet.GetWidth(pParent->getFramePrintArea()) ) InvalidateSize_(); InvalidatePos_(); + if (SwFrame *const pContent = ContainsContent()) + { // tdf#139687 invalidate possibly stale top margin (computed from previous frame) + pContent->InvalidatePrt_(); + } SwPageFrame *pPage = FindPageFrame(); InvalidatePage( pPage ); - if ( GetNext() ) - GetNext()->InvalidatePos_(); + if (SwFootnoteFrame *const pNext = static_cast<SwFootnoteFrame *>(GetNext())) + { + pNext->InvalidatePos_(); + if (SwFrame *const pContent = pNext->ContainsContent()) + { // tdf#139687 invalidate possibly stale top margin (computed from previous frame) + pContent->InvalidatePrt_(); + } + } if( aRectFnSet.GetHeight(getFrameArea()) ) pParent->Grow( aRectFnSet.GetHeight(getFrameArea()) ); @@ -2863,6 +2873,9 @@ bool SwContentFrame::MoveFootnoteCntFwd( bool bMakePage, SwFootnoteBossFrame *pO OSL_ENSURE( pTmp->IsTabFrame(), "GetNextSctLeaf: Wrong Type" ); pTmpNxt = static_cast<SwTabFrame*>(pTmp); } + // we will dereference pNewUp in the following MoveSubTree call + // so it certainly should not be deleted before that + SwFrameDeleteGuard aDeleteGuard(pNewUp); pTmpNxt->MoveSubTree( pTmpFootnote, pNewUp->GetNext() ); } } diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx index 04201e1c6f5e..62d78d4c38e6 100644 --- a/sw/source/core/layout/layact.cxx +++ b/sw/source/core/layout/layact.cxx @@ -62,6 +62,8 @@ #include <sortedobjs.hxx> #include <objectformatter.hxx> #include <fntcache.hxx> +#include <fmtanchr.hxx> +#include <comphelper/scopeguard.hxx> #include <vector> #include <tools/diagnose_ex.h> @@ -282,11 +284,12 @@ bool SwLayAction::IsInterrupt() void SwLayAction::Reset() { + SetAgain(false); m_pOptTab = nullptr; m_nStartTicks = std::clock(); m_nEndPage = m_nPreInvaPage = m_nCheckPageNum = USHRT_MAX; m_bPaint = m_bComplete = m_bWaitAllowed = m_bCheckPages = true; - m_bInterrupt = m_bAgain = m_bNextCycle = m_bCalcLayout = m_bReschedule = + m_bInterrupt = m_bNextCycle = m_bCalcLayout = m_bReschedule = m_bUpdateExpFields = m_bBrowseActionStop = false; m_pCurPage = nullptr; } @@ -303,7 +306,8 @@ bool SwLayAction::RemoveEmptyBrowserPages() do { if ( (pPage->GetSortedObjs() && pPage->GetSortedObjs()->size()) || - pPage->ContainsContent() ) + pPage->ContainsContent() || + pPage->FindFootnoteCont() ) pPage = static_cast<SwPageFrame*>(pPage->GetNext()); else { @@ -318,6 +322,53 @@ bool SwLayAction::RemoveEmptyBrowserPages() return bRet; } +void SwLayAction::SetAgain(bool bAgain) +{ + if (bAgain == m_bAgain) + return; + + m_bAgain = bAgain; + + assert(m_aFrameStack.size() == m_aFrameDeleteGuards.size()); + size_t nCount = m_aFrameStack.size(); + if (m_bAgain) + { + // LayAction::FormatLayout is now flagged to exit early and will avoid + // dereferencing any SwFrames in the stack of FormatLayouts so allow + // their deletion + for (size_t i = 0; i < nCount; ++i) + m_aFrameDeleteGuards[i].reset(); + } + else + { + // LayAction::FormatLayout is now continue normally and will + // dereference the top SwFrame in the stack of m_aFrameStack as each + // FormatLevel returns so disallow their deletion + for (size_t i = 0; i < nCount; ++i) + m_aFrameDeleteGuards[i] = std::make_unique<SwFrameDeleteGuard>(m_aFrameStack[i]); + } +} + +void SwLayAction::PushFormatLayout(SwFrame* pLow) +{ + /* Workaround crash seen in crashtesting with fdo53985-1.docx + + Lock pLow against getting deleted when it will be dereferenced + after FormatLayout + + If SetAgain is called to make SwLayAction exit early to avoid that + dereference, then it clears these guards + */ + m_aFrameStack.push_back(pLow); + m_aFrameDeleteGuards.push_back(std::make_unique<SwFrameDeleteGuard>(pLow)); +} + +void SwLayAction::PopFormatLayout() +{ + m_aFrameDeleteGuards.pop_back(); + m_aFrameStack.pop_back(); +} + void SwLayAction::Action(OutputDevice* pRenderContext) { m_bActionInProgress = true; @@ -344,12 +395,15 @@ void SwLayAction::Action(OutputDevice* pRenderContext) SetCheckPages( false ); InternalAction(pRenderContext); - m_bAgain |= RemoveEmptyBrowserPages(); + if (RemoveEmptyBrowserPages()) + SetAgain(true); while ( IsAgain() ) { - m_bAgain = m_bNextCycle = false; + SetAgain(false); + m_bNextCycle = false; InternalAction(pRenderContext); - m_bAgain |= RemoveEmptyBrowserPages(); + if (RemoveEmptyBrowserPages()) + SetAgain(true); } m_pRoot->DeleteEmptySct(); @@ -448,15 +502,19 @@ void SwLayAction::InternalAction(OutputDevice* pRenderContext) sal_uInt16 nPercentPageNum = 0; while ((!IsInterrupt() && pPage) || (m_nCheckPageNum != USHRT_MAX)) { - if (!pPage && m_nCheckPageNum != USHRT_MAX) + // note: this is the only place that consumes and resets m_nCheckPageNum + if ((IsInterrupt() || !pPage) && m_nCheckPageNum != USHRT_MAX) { - SwPageFrame *pPg = static_cast<SwPageFrame*>(m_pRoot->Lower()); - while (pPg && pPg->GetPhyPageNum() < m_nCheckPageNum) - pPg = static_cast<SwPageFrame*>(pPg->GetNext()); - if (pPg) - pPage = pPg; - if (!pPage) - break; + if (!pPage || m_nCheckPageNum < pPage->GetPhyPageNum()) + { + SwPageFrame *pPg = static_cast<SwPageFrame*>(m_pRoot->Lower()); + while (pPg && pPg->GetPhyPageNum() < m_nCheckPageNum) + pPg = static_cast<SwPageFrame*>(pPg->GetNext()); + if (pPg) + pPage = pPg; + if (!pPage) + break; + } SwPageFrame *pTmp = pPage->GetPrev() ? static_cast<SwPageFrame*>(pPage->GetPrev()) : pPage; @@ -637,7 +695,7 @@ void SwLayAction::InternalAction(OutputDevice* pRenderContext) { bool bOld = IsAgain(); m_pRoot->RemoveSuperfluous(); - m_bAgain = bOld; + SetAgain(bOld); } if ( IsAgain() ) { @@ -1370,7 +1428,11 @@ bool SwLayAction::FormatLayout( OutputDevice *pRenderContext, SwLayoutFrame *pLa } // Skip the ones already registered for deletion else if( !pLow->IsSctFrame() || static_cast<SwSectionFrame*>(pLow)->GetSection() ) + { + PushFormatLayout(pLow); bChanged |= FormatLayout( pRenderContext, static_cast<SwLayoutFrame*>(pLow), bAddRect ); + PopFormatLayout(); + } } else if ( m_pImp->GetShell()->IsPaintLocked() ) // Shortcut to minimize the cycles. With Lock, the @@ -1594,8 +1656,51 @@ bool SwLayAction::FormatLayoutTab( SwTabFrame *pTab, bool bAddRect ) return bChanged; } -bool SwLayAction::FormatContent( const SwPageFrame *pPage ) +bool SwLayAction::FormatContent(SwPageFrame *const pPage) { + ::comphelper::ScopeGuard g([this, pPage]() { + if (IsAgain()) + { + return; // pPage probably deleted + } + if (auto const* pObjs = pPage->GetSortedObjs()) + { + std::vector<std::pair<SwAnchoredObject*, SwPageFrame*>> moved; + for (auto const pObj : *pObjs) + { + assert(!pObj->AnchorFrame()->IsTextFrame() + || !static_cast<SwTextFrame const*>(pObj->AnchorFrame())->IsFollow()); + SwPageFrame *const pAnchorPage(pObj->AnchorFrame()->FindPageFrame()); + assert(pAnchorPage); + if (pAnchorPage != pPage + && pPage->GetPhyPageNum() < pAnchorPage->GetPhyPageNum() + && pObj->GetFrameFormat().GetAnchor().GetAnchorId() + != RndStdIds::FLY_AS_CHAR) + { + moved.emplace_back(pObj, pAnchorPage); + } + } + for (auto const& [pObj, pAnchorPage] : moved) + { + SAL_INFO("sw.layout", "SwLayAction::FormatContent: move anchored " << pObj << " from " << pPage->GetPhyPageNum() << " to " << pAnchorPage->GetPhyPageNum()); + pObj->RegisterAtPage(*pAnchorPage); + // tdf#143239 if the position remains valid, it may not be + // positioned again so would remain on the wrong page! + pObj->InvalidateObjPos(); + ::Notify_Background(pObj->GetDrawObj(), pPage, + pObj->GetObjRect(), PREP_FLY_LEAVE, false); + } + if (!moved.empty()) + { + pPage->InvalidateFlyLayout(); + if (auto *const pContent = pPage->FindLastBodyContent()) + { + pContent->InvalidateSize(); + } + } + } + }); + const SwContentFrame *pContent = pPage->ContainsContent(); const SwViewShell *pSh = m_pRoot->GetCurrShell(); const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode(); diff --git a/sw/source/core/layout/newfrm.cxx b/sw/source/core/layout/newfrm.cxx index 1d1f4597fcf2..39ea1c9c2bc0 100644 --- a/sw/source/core/layout/newfrm.cxx +++ b/sw/source/core/layout/newfrm.cxx @@ -502,6 +502,11 @@ void SwRootFrame::Init( SwFrameFormat* pFormat ) ::InsertCnt_( pLay, pDoc, aTmp.GetIndex(), true ); //Remove masters that haven't been replaced yet from the list. RemoveMasterObjs( mpDrawPage ); + + // 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(); + if( rSettingAccess.get(DocumentSettingId::GLOBAL_DOCUMENT) ) rFieldsAccess.UpdateRefFields(); //b6433357: Update page fields after loading diff --git a/sw/source/core/layout/objectformattertxtfrm.cxx b/sw/source/core/layout/objectformattertxtfrm.cxx index 1ba020a84901..57c3c7680da0 100644 --- a/sw/source/core/layout/objectformattertxtfrm.cxx +++ b/sw/source/core/layout/objectformattertxtfrm.cxx @@ -29,6 +29,7 @@ #include <fmtwrapinfluenceonobjpos.hxx> #include <fmtfollowtextflow.hxx> #include <layact.hxx> +#include <flyfrm.hxx> #include <ftnfrm.hxx> using namespace ::com::sun::star; @@ -224,11 +225,16 @@ bool SwObjectFormatterTextFrame::DoFormatObj( SwAnchoredObject& _rAnchoredObj, sal_uInt32 nToPageNum( 0 ); // #i43913# bool bDummy( false ); - // #i58182# - consider new method signature + bool bPageHasFlysAnchoredBelowThis(false); + // see how SwObjectFormatter::FormatObjsAtFrame_() checks + // "pPageFrameOfAnchor == &mrPageFrame" - only caller relevant for + // this subclass + assert(GetPageFrame().GetPhyPageNum() == GetPgNumOfCollected(nIdx)); if ( SwObjectFormatterTextFrame::CheckMovedFwdCondition( *GetCollectedObj( nIdx ), - GetPgNumOfCollected( nIdx ), + GetPageFrame(), IsCollectedAnchoredAtMaster( nIdx ), - nToPageNum, bDummy ) ) + nToPageNum, bDummy, + bPageHasFlysAnchoredBelowThis)) { // #i49987# - consider, that anchor frame // could already been marked to move forward. @@ -239,7 +245,12 @@ bool SwObjectFormatterTextFrame::DoFormatObj( SwAnchoredObject& _rAnchoredObj, rDoc, mrAnchorTextFrame, nMovedFwdToPageNum ) ) { if ( nMovedFwdToPageNum < nToPageNum ) - SwLayouter::RemoveMovedFwdFrame( rDoc, mrAnchorTextFrame ); + { + if (!bPageHasFlysAnchoredBelowThis) + { + SwLayouter::RemoveMovedFwdFrame(rDoc, mrAnchorTextFrame); + } + } else bInsert = false; } @@ -247,8 +258,11 @@ bool SwObjectFormatterTextFrame::DoFormatObj( SwAnchoredObject& _rAnchoredObj, { // Indicate that anchor text frame has to move forward and // invalidate its position to force a re-format. - SwLayouter::InsertMovedFwdFrame( rDoc, mrAnchorTextFrame, - nToPageNum ); + if (!bPageHasFlysAnchoredBelowThis) + { + SwLayouter::InsertMovedFwdFrame(rDoc, + mrAnchorTextFrame, nToPageNum); + } mrAnchorTextFrame.InvalidatePos(); // Indicate restart of the layout process @@ -293,7 +307,7 @@ bool SwObjectFormatterTextFrame::DoFormatObjs() { // notify layout action, thus is can restart the layout process on // a previous page. - GetLayAction()->SetAgain(); + GetLayAction()->SetAgain(true); } else { @@ -350,13 +364,14 @@ bool SwObjectFormatterTextFrame::DoFormatObjs() sal_uInt32 nToPageNum( 0 ); // #i43913# bool bInFollow( false ); + bool bPageHasFlysAnchoredBelowThis(false); SwAnchoredObject* pObj = nullptr; if ( !mrAnchorTextFrame.IsFollow() ) { pObj = GetFirstObjWithMovedFwdAnchor( // #i35017# - constant name has changed text::WrapInfluenceOnPosition::ONCE_CONCURRENT, - nToPageNum, bInFollow ); + nToPageNum, bInFollow, bPageHasFlysAnchoredBelowThis ); } // #i35911# if ( pObj && pObj->HasClearedEnvironment() ) @@ -377,14 +392,22 @@ bool SwObjectFormatterTextFrame::DoFormatObjs() rDoc, mrAnchorTextFrame, nTmpToPageNum ) ) { if ( nTmpToPageNum < pAnchorPageFrame->GetPhyPageNum() ) - SwLayouter::RemoveMovedFwdFrame( rDoc, mrAnchorTextFrame ); + { + if (!bPageHasFlysAnchoredBelowThis) + { + SwLayouter::RemoveMovedFwdFrame(rDoc, mrAnchorTextFrame); + } + } else bInsert = false; } if ( bInsert ) { - SwLayouter::InsertMovedFwdFrame( rDoc, mrAnchorTextFrame, - pAnchorPageFrame->GetPhyPageNum() ); + if (!bPageHasFlysAnchoredBelowThis) + { + SwLayouter::InsertMovedFwdFrame(rDoc, mrAnchorTextFrame, + pAnchorPageFrame->GetPhyPageNum()); + } mrAnchorTextFrame.InvalidatePos(); bSuccess = false; InvalidatePrevObjs( *pObj ); @@ -503,7 +526,8 @@ void SwObjectFormatterTextFrame::InvalidateFollowObjs( SwAnchoredObject& _rAncho SwAnchoredObject* SwObjectFormatterTextFrame::GetFirstObjWithMovedFwdAnchor( const sal_Int16 _nWrapInfluenceOnPosition, sal_uInt32& _noToPageNum, - bool& _boInFollow ) + bool& _boInFollow, + bool& o_rbPageHasFlysAnchoredBelowThis) { // #i35017# - constant names have changed OSL_ENSURE( _nWrapInfluenceOnPosition == text::WrapInfluenceOnPosition::ONCE_SUCCESSIVE || @@ -521,13 +545,17 @@ SwAnchoredObject* SwObjectFormatterTextFrame::GetFirstObjWithMovedFwdAnchor( // #i35017# - handle ITERATIVE as ONCE_SUCCESSIVE GetWrapInfluenceOnObjPos( true ) == _nWrapInfluenceOnPosition ) { + // see how SwObjectFormatter::FormatObjsAtFrame_() checks + // "pPageFrameOfAnchor == &mrPageFrame" - only caller relevant for + // this subclass + assert(GetPageFrame().GetPhyPageNum() == GetPgNumOfCollected(i)); // #i26945# - use new method <_CheckMovedFwdCondition(..)> // #i43913# - // #i58182# - consider new method signature if ( SwObjectFormatterTextFrame::CheckMovedFwdCondition( *GetCollectedObj( i ), - GetPgNumOfCollected( i ), + GetPageFrame(), IsCollectedAnchoredAtMaster( i ), - _noToPageNum, _boInFollow ) ) + _noToPageNum, _boInFollow, + o_rbPageHasFlysAnchoredBelowThis) ) { pRetAnchoredObj = pAnchoredObj; break; @@ -538,15 +566,49 @@ SwAnchoredObject* SwObjectFormatterTextFrame::GetFirstObjWithMovedFwdAnchor( return pRetAnchoredObj; } +static SwRowFrame const* FindTopLevelRowFrame(SwFrame const*const pFrame) +{ + SwRowFrame * pRow = const_cast<SwFrame*>(pFrame)->FindRowFrame(); + // looks like SwTabFrame has mbInfTab = true so go up 2 levels + while (pRow->GetUpper()->GetUpper()->IsInTab()) + { + pRow = pRow->GetUpper()->GetUpper()->FindRowFrame(); + } + return pRow; +} + +static SwContentFrame const* FindFrameInBody(SwAnchoredObject const& rAnchored) +{ + SwFrame const*const pAnchor(rAnchored.GetAnchorFrame()); + assert(pAnchor); + if (pAnchor->IsPageFrame() || pAnchor->FindFooterOrHeader()) + { + return nullptr; + } + if (pAnchor->IsInFly()) + { + return FindFrameInBody(*pAnchor->FindFlyFrame()); + } + if (pAnchor->IsInFootnote()) + { + return pAnchor->FindFootnoteFrame()->GetRef(); + } + assert(pAnchor->IsInDocBody()); + assert(pAnchor->IsContentFrame()); + return static_cast<SwContentFrame const*>(pAnchor); +} + // #i58182# // - replace private method by corresponding static public method bool SwObjectFormatterTextFrame::CheckMovedFwdCondition( SwAnchoredObject& _rAnchoredObj, - const sal_uInt32 _nFromPageNum, + SwPageFrame const& rFromPageFrame, const bool _bAnchoredAtMasterBeforeFormatAnchor, sal_uInt32& _noToPageNum, - bool& _boInFollow ) + bool& _boInFollow, + bool& o_rbPageHasFlysAnchoredBelowThis) { + const sal_uInt32 _nFromPageNum(rFromPageFrame.GetPhyPageNum()); bool bAnchorIsMovedForward( false ); SwPageFrame* pPageFrameOfAnchor = _rAnchoredObj.FindPageFrameOfAnchor(); @@ -621,6 +683,69 @@ bool SwObjectFormatterTextFrame::CheckMovedFwdCondition( } } + if (bAnchorIsMovedForward) + { + // tdf#138518 try to determine if there is a fly on page rFromPageFrame + // which is anchored in a frame that is "below" the anchor frame + // of _rAnchoredObj, such that it should move to the next page before + // _rAnchoredObj does + if (auto * pObjs = rFromPageFrame.GetSortedObjs()) + { + for (SwAnchoredObject *const pObj : *pObjs) + { + SwPageFrame const*const pObjAnchorPage(pObj->FindPageFrameOfAnchor()); + assert(pObjAnchorPage); + if ((pObjAnchorPage == &rFromPageFrame + ? _boInFollow // same-page but will move forward + : rFromPageFrame.GetPhyPageNum() < pObjAnchorPage->GetPhyPageNum()) + && pObj->GetFrameFormat().GetAnchor().GetAnchorId() + != RndStdIds::FLY_AS_CHAR) + { + if (pPageFrameOfAnchor->GetPhyPageNum() < pObjAnchorPage->GetPhyPageNum()) + { + SAL_INFO("sw.layout", "SwObjectFormatterTextFrame::CheckMovedFwdCondition(): o_rbPageHasFlysAnchoredBelowThis because next page"); + o_rbPageHasFlysAnchoredBelowThis = true; + break; + } + // on same page: check if it's in next-chain in the document body + // (in case both are in the same fly the flag must not be + // set because the whole fly moves at once) + SwContentFrame const*const pInBodyFrameObj(FindFrameInBody(*pObj)); + SwContentFrame const*const pInBodyFrameAnchoredObj(FindFrameInBody(_rAnchoredObj)); + if (pInBodyFrameObj && pInBodyFrameAnchoredObj) + { + bool isBreakMore(false); + // currently this ignores index of at-char flys + for (SwContentFrame const* pContentFrame = pInBodyFrameAnchoredObj->FindNextCnt(); + pContentFrame; + pContentFrame = pContentFrame->FindNextCnt()) + { + if (pInBodyFrameObj == pContentFrame) + { + // subsequent cells in a row are not automatically + // "below" and the row could potentially be split + // TODO refine check if needed + if (!pInBodyFrameAnchoredObj->IsInTab() + || FindTopLevelRowFrame(pInBodyFrameAnchoredObj) + != FindTopLevelRowFrame(pInBodyFrameAnchoredObj)) + { // anchored in next chain on same page + SAL_INFO("sw.layout", "SwObjectFormatterTextFrame::CheckMovedFwdCondition(): o_rbPageHasFlysAnchoredBelowThis because next chain on same page"); + o_rbPageHasFlysAnchoredBelowThis = true; + isBreakMore = true; + } + break; + } + } + if (isBreakMore) + { + break; + } + } + } + } + } + } + return bAnchorIsMovedForward; } @@ -641,6 +766,7 @@ static void lcl_FormatContentOfLayoutFrame( SwLayoutFrame* pLayFrame, if ( pLowerFrame->IsLayoutFrame() ) { SwFrameDeleteGuard aCrudeHack(pLowerFrame); // ??? any issue setting this for non-footnote frames? + // prevent moving footnotes by formatting if they are already being moved lcl_FormatContentOfLayoutFrame( static_cast<SwLayoutFrame*>(pLowerFrame), pLastLowerFrame ); } @@ -686,21 +812,46 @@ void SwObjectFormatterTextFrame::FormatAnchorFrameAndItsPrevs( SwTextFrame& _rAn // for follow text frames. if ( !_rAnchorTextFrame.IsFollow() ) { + // In case the anchor frame is in a column or section, format its + // previous frames first - but don't jump out of the current layout + // environment, e.g. from footnotes into the footnote boss. + SwFrame * pSectFrame(nullptr); + SwFrame * pColFrameOfAnchor(nullptr); + for (SwFrame* pUpper = _rAnchorTextFrame.GetUpper(); + pUpper != nullptr; pUpper = pUpper->GetUpper()) + { + if (pUpper->IsCellFrame()) + { + break; // apparently nothing to be done? + } + if (pUpper->IsFootnoteFrame()) + { + SAL_INFO_IF(pColFrameOfAnchor == nullptr && pUpper->FindColFrame(), + "sw.layout", "tdf#122894 skipping column for footnote in column"); + break; // stop: prevent crash in case footnotes are being moved + } + if (pUpper->IsSctFrame()) + { + pColFrameOfAnchor = nullptr; + pSectFrame = pUpper; + break; + } + if (pColFrameOfAnchor != nullptr) + { // parent of column not a section frame => column not in section + break; + } + if (pUpper->IsColumnFrame()) + { + pColFrameOfAnchor = pUpper; + } + } + // if anchor frame is directly inside a section, format this section and // its previous frames. // Note: It's a very simple format without formatting objects. - if ( _rAnchorTextFrame.IsInSct() ) + if (pSectFrame) { - SwFrame* pSectFrame = _rAnchorTextFrame.GetUpper(); - while ( pSectFrame ) - { - if ( pSectFrame->IsSctFrame() || pSectFrame->IsCellFrame() ) - { - break; - } - pSectFrame = pSectFrame->GetUpper(); - } - if ( pSectFrame && pSectFrame->IsSctFrame() ) + assert(pSectFrame->IsSctFrame()); { SwFrameDeleteGuard aDeleteGuard(&_rAnchorTextFrame); // #i44049# @@ -711,6 +862,8 @@ void SwObjectFormatterTextFrame::FormatAnchorFrameAndItsPrevs( SwTextFrame& _rAn // Thus, check for valid <pFrame>. while ( pFrame && pFrame != pSectFrame ) { + SwFrameDeleteGuard aDeleteFrameGuard(pFrame); + if ( pFrame->IsLayoutFrame() ) lcl_FormatContentOfLayoutFrame( static_cast<SwLayoutFrame*>(pFrame) ); else @@ -728,9 +881,9 @@ void SwObjectFormatterTextFrame::FormatAnchorFrameAndItsPrevs( SwTextFrame& _rAn // #i40140# - if anchor frame is inside a column, // format the content of the previous columns. // Note: It's a very simple format without formatting objects. - SwFrame* pColFrameOfAnchor = _rAnchorTextFrame.FindColFrame(); - if ( pColFrameOfAnchor ) + if (pColFrameOfAnchor) { + assert(pColFrameOfAnchor->IsColumnFrame()); // #i44049# _rAnchorTextFrame.LockJoin(); SwFrame* pColFrame = pColFrameOfAnchor->GetUpper()->GetLower(); diff --git a/sw/source/core/layout/objectformattertxtfrm.hxx b/sw/source/core/layout/objectformattertxtfrm.hxx index cf3b955addb1..ae7b2efe227e 100644 --- a/sw/source/core/layout/objectformattertxtfrm.hxx +++ b/sw/source/core/layout/objectformattertxtfrm.hxx @@ -96,7 +96,8 @@ class SwObjectFormatterTextFrame : public SwObjectFormatter SwAnchoredObject* GetFirstObjWithMovedFwdAnchor( const sal_Int16 _nWrapInfluenceOnPosition, sal_uInt32& _noToPageNum, - bool& _boInFollow ); + bool& _boInFollow, + bool& o_rbPageHasFlysAnchoredBelowThis); /** method to format the anchor frame for checking of the move forward condition @@ -169,15 +170,19 @@ class SwObjectFormatterTextFrame : public SwObjectFormatter output parameter - boolean, indicating that anchor text frame is currently on the same page, but it's a follow of in a follow row, which will move forward. value only relevant, if method return <true>. + @param o_rbPageHasFlysAnchoredBelowThis + output parameter - indicates that the page has flys anchored + somewhere below the anchor of the passed _rAnchoredObj @return boolean indicating, if 'anchor is moved forward' */ static bool CheckMovedFwdCondition( SwAnchoredObject& _rAnchoredObj, - const sal_uInt32 _nFromPageNum, + SwPageFrame const& rFromPageFrame, const bool _bAnchoredAtMasterBeforeFormatAnchor, sal_uInt32& _noToPageNum, - bool& _boInFollow ); + bool& _boInFollow, + bool& o_rbPageHasFlysAnchoredBelowThis); }; #endif diff --git a/sw/source/core/layout/pagechg.cxx b/sw/source/core/layout/pagechg.cxx index a684b1602a1a..b1a04f0890c6 100644 --- a/sw/source/core/layout/pagechg.cxx +++ b/sw/source/core/layout/pagechg.cxx @@ -284,7 +284,7 @@ void SwPageFrame::DestroyImpl() SwViewShellImp *pImp = pSh->Imp(); pImp->SetFirstVisPageInvalid(); if ( pImp->IsAction() ) - pImp->GetLayAction().SetAgain(); + pImp->GetLayAction().SetAgain(true); // #i9719# - retouche area of page // including border and shadow area. const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT); @@ -990,11 +990,68 @@ void SwPageFrame::PrepareRegisterChg() } } +namespace sw { + +/// check if there's content on the page that requires it to exist +bool IsPageFrameEmpty(SwPageFrame const& rPage) +{ + bool bExistEssentialObjs = (nullptr != rPage.GetSortedObjs()); + if (bExistEssentialObjs) + { + // Only because the page has Flys does not mean that it is needed. If all Flys are + // attached to generic content it is also superfluous (checking DocBody should be enough) + // OD 19.06.2003 - consider that drawing objects in + // header/footer are supported now. + bool bOnlySuperfluousObjs = true; + SwSortedObjs const& rObjs = *rPage.GetSortedObjs(); + for (size_t i = 0; bOnlySuperfluousObjs && i < rObjs.size(); ++i) + { + // #i28701# + SwAnchoredObject* pAnchoredObj = rObjs[i]; + // do not consider hidden objects + if ( rPage.GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( + pAnchoredObj->GetDrawObj()->GetLayer() ) && + !pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() ) + { + bOnlySuperfluousObjs = false; + } + } + bExistEssentialObjs = !bOnlySuperfluousObjs; + } + + // optimization: check first if essential objects exist. + const SwLayoutFrame* pBody = nullptr; + if ( bExistEssentialObjs || + rPage.FindFootnoteCont() || + (nullptr != (pBody = rPage.FindBodyCont()) && + ( pBody->ContainsContent() || + // check for section frames that are being formatted on the stack + rPage.ContainsDeleteForbiddenLayFrame() || + // #i47580# + // Do not delete page if there's an empty tabframe + // left. I think it might be correct to use ContainsAny() + // instead of ContainsContent() to cover the empty-table-case, + // but I'm not fully sure, since ContainsAny() also returns + // SectionFrames. Therefore I prefer to do it the safe way: + ( pBody->Lower() && pBody->Lower()->IsTabFrame() ) ) ) ) + { + return false; + } + else + { + return true; + } +} + +} // namespace sw + //FIXME: provide missing documentation /** Check all pages (starting from the given one) if they use the appropriate frame format. * * If "wrong" pages are found, try to fix this as simple as possible. * + * Also delete pages that don't have content on them. + * * @param pStart the page from where to start searching * @param bNotifyFields * @param ppPrev @@ -1032,7 +1089,10 @@ void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFra SwPageFrame *pNextPage = static_cast<SwPageFrame*>(pPage->GetNext()); SwPageDesc *pDesc = pPage->FindPageDesc(); + /// page is intentionally empty page bool bIsEmpty = pPage->IsEmptyPage(); + // false for intentionally empty pages, they need additional check + bool isPageFrameEmpty(!bIsEmpty && sw::IsPageFrameEmpty(*pPage)); bool bIsOdd = pPage->OnRightPage(); bool bWantOdd = pPage->WannaRightPage(); bool bFirst = pPage->OnFirstPage(); @@ -1129,6 +1189,7 @@ void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFra pTmp->Paste( pRoot, pPage ); pTmp->PreparePage( false ); pPage = pTmp; + isPageFrameEmpty = false; // don't delete it right away! } else if ( pPage->GetPageDesc() != pDesc ) //4. { @@ -1172,16 +1233,21 @@ void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFra } #endif } - if ( bIsEmpty ) + assert(!bIsEmpty || !isPageFrameEmpty); + if (bIsEmpty || isPageFrameEmpty) { // It also might be that an empty page is not needed at all. // However, the algorithm above cannot determine that. It is not needed if the following // page can live without it. Do obtain that information, we need to dig deeper... SwPageFrame *pPg = static_cast<SwPageFrame*>(pPage->GetNext()); - if( !pPg || pPage->OnRightPage() == pPg->WannaRightPage() ) + if (isPageFrameEmpty || !pPg || pPage->OnRightPage() == pPg->WannaRightPage()) { // The following page can find a FrameFormat or has no successor -> empty page not needed SwPageFrame *pTmp = static_cast<SwPageFrame*>(pPage->GetNext()); + if (isPageFrameEmpty && pPage->GetPrev()) + { // check previous *again* vs. its new next! see "ooo321_stylepagenumber.odt" + pTmp = static_cast<SwPageFrame*>(pPage->GetPrev()); + } pPage->Cut(); bool bUpdatePrev = false; if (ppPrev && *ppPrev == pPage) @@ -1441,44 +1507,7 @@ void SwRootFrame::RemoveSuperfluous() // Check the corresponding last page if it is empty and stop loop at the last non-empty page. do { - bool bExistEssentialObjs = ( nullptr != pPage->GetSortedObjs() ); - if ( bExistEssentialObjs ) - { - // Only because the page has Flys does not mean that it is needed. If all Flys are - // attached to generic content it is also superfluous (checking DocBody should be enough) - // OD 19.06.2003 #108784# - consider that drawing objects in - // header/footer are supported now. - bool bOnlySuperfluosObjs = true; - SwSortedObjs &rObjs = *pPage->GetSortedObjs(); - for ( size_t i = 0; bOnlySuperfluosObjs && i < rObjs.size(); ++i ) - { - // #i28701# - SwAnchoredObject* pAnchoredObj = rObjs[i]; - // OD 2004-01-19 #110582# - do not consider hidden objects - if ( pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( - pAnchoredObj->GetDrawObj()->GetLayer() ) && - !pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() ) - { - bOnlySuperfluosObjs = false; - } - } - bExistEssentialObjs = !bOnlySuperfluosObjs; - } - - // OD 19.06.2003 #108784# - optimization: check first, if essential objects - // exists. - const SwLayoutFrame* pBody = nullptr; - if ( bExistEssentialObjs || - pPage->FindFootnoteCont() || - ( nullptr != ( pBody = pPage->FindBodyCont() ) && - ( pBody->ContainsContent() || - // #i47580# - // Do not delete page if there's an empty tabframe - // left. I think it might be correct to use ContainsAny() - // instead of ContainsContent() to cover the empty-table-case, - // but I'm not fully sure, since ContainsAny() also returns - // SectionFrames. Therefore I prefer to do it the safe way: - ( pBody->Lower() && pBody->Lower()->IsTabFrame() ) ) ) ) + if (!sw::IsPageFrameEmpty(*pPage)) { if ( pPage->IsFootnotePage() ) { @@ -1976,6 +2005,11 @@ static void lcl_MoveAllLowerObjs( SwFrame* pFrame, const Point& rOffset ) { SwFlyFrame* pFlyFrame( static_cast<SwFlyFrame*>(pAnchoredObj) ); lcl_MoveAllLowers( pFlyFrame, rOffset ); + // tdf#138785 update position specific to as-char flys + if (pFlyFrame->IsFlyInContentFrame()) + { + static_cast<SwFlyInContentFrame*>(pFlyFrame)->AddRefOfst(rOffset); + } pFlyFrame->NotifyDrawObj(); // --> let the active embedded object be moved SwFrame* pLower = pFlyFrame->Lower(); diff --git a/sw/source/core/layout/sectfrm.cxx b/sw/source/core/layout/sectfrm.cxx index 524562585bd1..48debbcc399a 100644 --- a/sw/source/core/layout/sectfrm.cxx +++ b/sw/source/core/layout/sectfrm.cxx @@ -2863,7 +2863,8 @@ void SwRootFrame::DeleteEmptySct_() mpDestroy->erase( mpDestroy->begin() ); OSL_ENSURE( !pSect->IsColLocked() && !pSect->IsJoinLocked(), "DeleteEmptySct: Locked SectionFrame" ); - if( !pSect->getFrameArea().HasArea() && !pSect->ContainsContent() ) + SAL_WARN_IF(pSect->IsDeleteForbidden(), "sw.layout", "not allowed delete SwFrame"); + if( !pSect->getFrameArea().HasArea() && !pSect->ContainsContent() && !pSect->IsDeleteForbidden() ) { SwLayoutFrame* pUp = pSect->GetUpper(); pSect->RemoveFromLayout(); diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx index 719f08e5a363..f39250dcc67b 100644 --- a/sw/source/core/layout/tabfrm.cxx +++ b/sw/source/core/layout/tabfrm.cxx @@ -23,6 +23,7 @@ #include <viewimp.hxx> #include <fesh.hxx> #include <swtable.hxx> +#include <deletelistener.hxx> #include <dflyobj.hxx> #include <anchoreddrawobject.hxx> #include <fmtanchr.hxx> @@ -73,6 +74,7 @@ SwTabFrame::SwTabFrame( SwTable &rTab, SwFrame* pSib ) , m_bCalcLowers(false) , m_bLowersFormatted(false) , m_bLockBackMove(false) + , m_bWantBackMove(false) , m_bResizeHTMLTable(false) , m_bONECalcLowers(false) , m_bHasFollowFlowLine(false) @@ -112,6 +114,7 @@ SwTabFrame::SwTabFrame( SwTabFrame &rTab ) , m_bCalcLowers(false) , m_bLowersFormatted(false) , m_bLockBackMove(false) + , m_bWantBackMove(false) , m_bResizeHTMLTable(false) , m_bONECalcLowers(false) , m_bHasFollowFlowLine(false) @@ -1343,13 +1346,30 @@ bool SwTabFrame::Split( const SwTwips nCutPos, bool bTryToSplit, bool bTableRowK return bRet; } +namespace +{ + bool CanDeleteFollow(SwTabFrame *pFoll) + { + if (pFoll->IsJoinLocked()) + return false; + + if (pFoll->IsDeleteForbidden()) + { + SAL_WARN("sw.layout", "Delete Forbidden"); + return false; + } + + return true; + } +} + void SwTabFrame::Join() { OSL_ENSURE( !HasFollowFlowLine(), "Joining follow flow line" ); SwTabFrame *pFoll = GetFollow(); - if (pFoll && !pFoll->IsJoinLocked()) + if (pFoll && CanDeleteFollow(pFoll)) { SwRectFnSet aRectFnSet(this); pFoll->Cut(); //Cut out first to avoid unnecessary notifications. @@ -1578,6 +1598,8 @@ static bool lcl_InnerCalcLayout( SwFrame *pFrame, if ( pFrame->IsLayoutFrame() && ( !_bOnlyRowsAndCells || pFrame->IsRowFrame() || pFrame->IsCellFrame() ) ) { + SwFrameDeleteGuard aDeleteGuard(pFrame); + // #130744# An invalid locked table frame will // not be calculated => It will not become valid => // Loop in lcl_RecalcRow(). Therefore we do not consider them for bRet. @@ -1837,7 +1859,7 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) // is not locked. Otherwise, join will not be performed and this loop // will be endless. while ( GetNext() && GetNext() == GetFollow() && - !GetFollow()->IsJoinLocked() + CanDeleteFollow(GetFollow()) ) { if ( HasFollowFlowLine() ) @@ -1990,8 +2012,6 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) pAccess.reset(); m_bCalcLowers |= pLayout->Resize( pLayout->GetBrowseWidthByTabFrame( *this ) ); - pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); - pAttrs = pAccess->Get(); } setFramePrintAreaValid(false); @@ -2020,6 +2040,12 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) const long nOldPrtWidth = aRectFnSet.GetWidth(getFramePrintArea()); const long nOldFrameWidth = aRectFnSet.GetWidth(getFrameArea()); const Point aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea()); + + if (!pAccess) + { + pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); + pAttrs = pAccess->Get(); + } Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs ); SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout(); @@ -2030,8 +2056,6 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) pAccess.reset(); m_bCalcLowers |= pLayout->Resize( pLayout->GetBrowseWidthByTabFrame( *this ) ); - pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); - pAttrs = pAccess->Get(); } if ( aOldPrtPos != aRectFnSet.GetPos(getFramePrintArea()) ) aNotify.SetLowersComplete( false ); @@ -2059,12 +2083,18 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) } SwFootnoteBossFrame *pOldBoss = bFootnotesInDoc ? FindFootnoteBossFrame( true ) : nullptr; bool bReformat; + std::optional<SfxDeleteListener> oDeleteListener; + if (pOldBoss) + oDeleteListener.emplace(*pOldBoss); + SwFrameDeleteGuard g(this); if ( MoveBwd( bReformat ) ) { + SAL_WARN_IF(oDeleteListener && oDeleteListener->WasDeleted(), "sw.layout", "SwFootnoteBossFrame unexpectedly deleted"); + aRectFnSet.Refresh(this); bMovedBwd = true; aNotify.SetLowersComplete( false ); - if ( bFootnotesInDoc ) + if (bFootnotesInDoc && !oDeleteListener->WasDeleted()) MoveLowerFootnotes( nullptr, pOldBoss, nullptr, true ); if ( bReformat || bKeep ) { @@ -2079,15 +2109,22 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) pAccess.reset(); m_bCalcLowers |= pHTMLLayout->Resize( pHTMLLayout->GetBrowseWidthByTabFrame( *this ) ); + } + + setFramePrintAreaValid(false); + if (!pAccess) + { pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); pAttrs = pAccess->Get(); } - - setFramePrintAreaValid(false); Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs ); } + + pAccess.reset(); + lcl_RecalcTable( *this, nullptr, aNotify ); + m_bLowersFormatted = true; if ( bKeep && KEEPTAB ) { @@ -2251,11 +2288,18 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) // 6. There is no section change behind the table (see IsKeep) // 7. The last table row wants to keep with its next. const SwRowFrame* pLastRow = static_cast<const SwRowFrame*>(GetLastLower()); - if (pLastRow - && IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), true) - && pLastRow->ShouldRowKeepWithNext()) + if (pLastRow) { - bFormat = true; + if (!pAccess) + { + pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); + pAttrs = pAccess->Get(); + } + if (IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), true) + && pLastRow->ShouldRowKeepWithNext()) + { + bFormat = true; + } } } @@ -2269,9 +2313,6 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) // is found, get its first content. const SwFrame* pTmpNxt = sw_FormatNextContentForKeep( this ); - pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); - pAttrs = pAccess->Get(); - // The last row wants to keep with the frame behind the table. // Check if the next frame is on a different page and valid. // In this case we do a magic trick: @@ -2512,9 +2553,6 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) GetFollow()->MakeAll(pRenderContext); - pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); - pAttrs = pAccess->Get(); - GetFollow()->SetLowersFormatted(false); // #i43913# - lock follow table // to avoid its formatting during the format of @@ -3513,9 +3551,17 @@ bool SwTabFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool &rReformat ) } else if (!m_bLockBackMove) bMoveAnyway = true; + else + { + m_bWantBackMove = true; + } } else if (!m_bLockBackMove) bMoveAnyway = true; + else + { + m_bWantBackMove = true; + } if ( bMoveAnyway ) { @@ -3527,7 +3573,7 @@ bool SwTabFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool &rReformat ) // This frame fits into pNewUpper in case it has no space, but this // frame is empty. bFits = nSpace >= 0; - if (!m_bLockBackMove && bFits) + if (bFits) { // #i26945# - check, if follow flow line // contains frame, which are moved forward due to its object @@ -3546,7 +3592,17 @@ bool SwTabFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool &rReformat ) // 'return nHeight <= nSpace' to 'return nTmpHeight < nSpace'. // This obviously results in problems with table frames in // sections. Remember: Every twip is sacred. - return nTmpHeight <= nSpace; + if (nTmpHeight <= nSpace) + { + if (m_bLockBackMove) + { + m_bWantBackMove = true; + } + else + { + return true; + } + } } } return false; @@ -5506,9 +5562,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/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx index f17a7ba68870..14aa081621f1 100644 --- a/sw/source/core/layout/wsfrm.cxx +++ b/sw/source/core/layout/wsfrm.cxx @@ -57,6 +57,7 @@ #include <sortedobjs.hxx> #include <frmatr.hxx> #include <frmtool.hxx> +#include <layact.hxx> #include <ndtxt.hxx> // RotateFlyFrame3 @@ -317,7 +318,7 @@ SwFrame::SwFrame( SwModify *pMod, SwFrame* pSib ) mbInfSct ( false ), mbColLocked(false), m_isInDestroy(false), - mbForbidDelete(false) + mnForbidDelete(0) { OSL_ENSURE( pMod, "No frame format given." ); } @@ -1200,6 +1201,23 @@ void SwContentFrame::Cut() if ( pRoot ) { pRoot->SetSuperfluous(); + // RemoveSuperfluous can only remove empty pages at the end; + // find if there are pages without content following pPage + // and if so request a call to CheckPageDescs() + SwViewShell *pSh = pRoot->GetCurrShell(); + // tdf#152983 pPage is null when called from SwHeadFootFrame ctor + if (pPage && pSh && pSh->Imp()->IsAction()) + { + SwPageFrame const* pNext(pPage); + while ((pNext = static_cast<SwPageFrame const*>(pNext->GetNext()))) + { + if (!sw::IsPageFrameEmpty(*pNext) && !pNext->IsFootnotePage()) + { + pSh->Imp()->GetLayAction().SetCheckPageNum(pPage->GetPhyPageNum()); + break; + } + } + } GetUpper()->SetCompletePaint(); GetUpper()->InvalidatePage( pPage ); } diff --git a/sw/source/core/ole/ndole.cxx b/sw/source/core/ole/ndole.cxx index 9000d6ef1b9e..8ff9b35967ce 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) { - uno::Reference< embed::XLinkageSupport > xLinkSupport( maOLEObj.m_xOLERef.GetObject(), uno::UNO_QUERY_THROW ); - if ( xLinkSupport->isLink() ) + if (xLinkSupport->isLink()) + aLinkURL = xLinkSupport->getLinkURL(); + } + else + { + // 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() @@ -1167,7 +1238,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 +1268,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/frmform.cxx b/sw/source/core/text/frmform.cxx index 69db90b6502d..3720c3117f9b 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/itratr.cxx b/sw/source/core/text/itratr.cxx index 4933bce92c9f..1a60ff2b42d6 100644 --- a/sw/source/core/text/itratr.cxx +++ b/sw/source/core/text/itratr.cxx @@ -447,7 +447,7 @@ static void InsertCharAttrs(SfxPoolItem const** pAttrs, SfxItemSet const& rItems } else if (nWhich == RES_TXTATR_UNKNOWN_CONTAINER) { - pAttrs[RES_CHRATR_END] = pItem; + pAttrs[RES_CHRATR_END - RES_CHRATR_BEGIN] = pItem; } } } diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx index f3178c2c7233..75cfc1e7e624 100644 --- a/sw/source/core/text/itrform2.cxx +++ b/sw/source/core/text/itrform2.cxx @@ -149,6 +149,16 @@ sal_uInt16 SwTextFormatter::GetFrameRstHeight() const return sal_uInt16( nHeight ); } +bool SwTextFormatter::ClearIfIsFirstOfBorderMerge(const SwLinePortion* pPortion) +{ + if (pPortion == m_pFirstOfBorderMerge) + { + m_pFirstOfBorderMerge = nullptr; + return true; + } + return false; +} + SwLinePortion *SwTextFormatter::Underflow( SwTextFormatInfo &rInf ) { // Save values and initialize rInf @@ -277,11 +287,8 @@ SwLinePortion *SwTextFormatter::Underflow( SwTextFormatInfo &rInf ) SwLinePortion* pNext = pPor->GetNextPortion(); while (pNext) { - if (pNext == m_pFirstOfBorderMerge) - { - m_pFirstOfBorderMerge = nullptr; + if (ClearIfIsFirstOfBorderMerge(pNext)) break; - } pNext = pNext->GetNextPortion(); } pPor->Truncate(); @@ -2516,7 +2523,11 @@ SwFlyCntPortion *SwTextFormatter::NewFlyCntPortion( SwTextFormatInfo &rInf, SwFlyInContentFrame *pFly; SwFrameFormat* pFrameFormat = static_cast<SwTextFlyCnt*>(pHint)->GetFlyCnt().GetFrameFormat(); if( RES_FLYFRMFMT == pFrameFormat->Which() ) + { + // set Lock pFrame to avoid m_pCurr getting deleted + TextFrameLockGuard aGuard(m_pFrame); pFly = static_cast<SwTextFlyCnt*>(pHint)->GetFlyFrame(pFrame); + } else pFly = nullptr; // aBase is the document-global position, from which the new extra portion is placed diff --git a/sw/source/core/text/itrform2.hxx b/sw/source/core/text/itrform2.hxx index c9a14f566741..ceabbabeb47b 100644 --- a/sw/source/core/text/itrform2.hxx +++ b/sw/source/core/text/itrform2.hxx @@ -238,6 +238,8 @@ public: * @param rInf contain information **/ void MergeCharacterBorder( SwLinePortion& rPortion, SwLinePortion const *pPrev, SwTextFormatInfo& rInf ); + + bool ClearIfIsFirstOfBorderMerge(SwLinePortion const *pPortion); }; #endif diff --git a/sw/source/core/text/porfld.cxx b/sw/source/core/text/porfld.cxx index a5eb9b99ce9b..b1c7003764a7 100644 --- a/sw/source/core/text/porfld.cxx +++ b/sw/source/core/text/porfld.cxx @@ -175,10 +175,18 @@ SwFieldSlot::SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pP pInf->SetFakeLineStart( nIdx > pInf->GetLineStart() ); pInf->SetIdx(TextFrameIndex(0)); } - else if (nIdx < TextFrameIndex(pOldText->getLength())) + else { - sal_Int32 const nFieldLen(pPor->GetFieldLen()); - aText = (*pOldText).replaceAt(sal_Int32(nIdx), nFieldLen, aText); + TextFrameIndex nEnd(pOldText->getLength()); + if (nIdx < nEnd) + { + sal_Int32 const nFieldLen(pPor->GetFieldLen()); + aText = (*pOldText).replaceAt(sal_Int32(nIdx), nFieldLen, aText); + } + else if (nIdx == nEnd) + aText = *pOldText + aText; + else + SAL_WARN("sw.core", "SwFieldSlot bad SwFieldPortion index."); } pInf->SetText( aText ); } @@ -1063,6 +1071,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 bdb00652bbcc..e3d442509032 100644 --- a/sw/source/core/text/porfld.hxx +++ b/sw/source/core/text/porfld.hxx @@ -203,7 +203,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 2bb9e87bd2c9..5e0f75dada94 100644 --- a/sw/source/core/text/porlay.cxx +++ b/sw/source/core/text/porlay.cxx @@ -395,7 +395,9 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf ) if( !GetAscent() ) SetAscent( pPos->GetAscent() ); } - delete pLast->Cut( pPos ); + SwLinePortion* pPortion = pLast->Cut( pPos ); + rLine.ClearIfIsFirstOfBorderMerge(pPortion); + delete pPortion; pPos = pLast->GetNextPortion(); continue; } @@ -2286,29 +2288,9 @@ void SwScriptInfo::selectHiddenTextProperty(const SwTextNode& rNode, MultiSelect const sw::mark::IMark* pMark = pIndex->GetMark(); const sw::mark::IBookmark* pBookmark = dynamic_cast<const sw::mark::IBookmark*>(pMark); - bool bHide = false; + // condition is evaluated in DocumentFieldsManager::UpdateExpFields() if (pBookmark && pBookmark->IsHidden()) { - // bookmark is marked as hidden - bHide = true; - - // bookmark is marked as hidden with conditions - if (!pBookmark->GetHideCondition().isEmpty()) - { - SwDoc& rDoc = *const_cast<SwDoc*>(rNode.GetDoc()); - SwCalc aCalc(rDoc); - rDoc.getIDocumentFieldsAccess().FieldsToCalc(aCalc, rNode.GetIndex(), USHRT_MAX); - - SwSbxValue aValue = aCalc.Calculate(pBookmark->GetHideCondition()); - if(!aValue.IsVoidValue()) - { - bHide = aValue.GetBool(); - } - } - } - - if (bHide) - { // intersect bookmark range with textnode range and add the intersection to rHiddenMulti const sal_Int32 nSt = pBookmark->GetMarkStart().nContent.GetIndex(); diff --git a/sw/source/core/text/txtfrm.cxx b/sw/source/core/text/txtfrm.cxx index 17636cda44e3..8d478867709e 100644 --- a/sw/source/core/text/txtfrm.cxx +++ b/sw/source/core/text/txtfrm.cxx @@ -1292,6 +1292,8 @@ void SwTextFrame::SetMergedPara(std::unique_ptr<sw::MergedPara> p) pFirst->Add(this); // must register at node again } } + // postcondition: frame must be listening somewhere + assert(m_pMergedPara || GetDep()); } const OUString& SwTextFrame::GetText() const @@ -2970,7 +2972,13 @@ bool SwTextFrame::Prepare( const PrepareHint ePrep, const void* pVoid, if( aTextFly.IsOn() ) { // Does any free-flying frame overlap? - bFormat = aTextFly.Relax() || IsUndersized(); + const bool bRelaxed = aTextFly.Relax(); + bFormat = bRelaxed || IsUndersized(); + if (bRelaxed) + { + // It's possible that pPara was deleted above; retrieve it again + pPara = aAccess.GetPara(); + } } } } diff --git a/sw/source/core/text/xmldump.cxx b/sw/source/core/text/xmldump.cxx index 20f61111126a..147ed5a97eb7 100644 --- a/sw/source/core/text/xmldump.cxx +++ b/sw/source/core/text/xmldump.cxx @@ -15,6 +15,7 @@ #include <pagefrm.hxx> #include <txtfrm.hxx> #include <cellfrm.hxx> +#include <flyfrm.hxx> #include <hffrm.hxx> #include <rootfrm.hxx> #include <editsh.hxx> @@ -67,6 +68,7 @@ class XmlPortionDumper:public SwPortionHandler case PortionType::Meta: return "PortionType::Meta"; case PortionType::FieldMark: return "PortionType::FieldMark"; case PortionType::FieldFormCheckbox: return "PortionType::FieldFormCheckbox"; + case PortionType::InputField: return "PortionType::InputField"; case PortionType::Expand: return "PortionType::Expand"; case PortionType::Blank: return "PortionType::Blank"; @@ -326,7 +328,7 @@ void SwFrame::dumpAsXml( xmlTextWriterPtr writer ) const SwView* pView = static_cast<SwView*>(SfxViewShell::GetFirst(true, checkSfxViewShell<SwView>)); while (pView) { - if (pView->GetObjectShell() == pRootFrame->GetCurrShell()->GetSfxViewShell()->GetObjectShell()) + if (pRootFrame->GetCurrShell()->GetSfxViewShell() && pView->GetObjectShell() == pRootFrame->GetCurrShell()->GetSfxViewShell()->GetObjectShell()) pView->dumpAsXml(writer); pView = static_cast<SwView*>(SfxViewShell::GetNext(*pView, true, checkSfxViewShell<SwView>)); } @@ -343,6 +345,35 @@ void SwFrame::dumpAsXml( xmlTextWriterPtr writer ) const xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidLayout"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidLayout()).getStr())); xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidContent"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidContent()).getStr())); xmlTextWriterEndElement(writer); + xmlTextWriterStartElement(writer, BAD_CAST("page_info")); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("phyNum"), "%d", pPageFrame->GetPhyPageNum()); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("virtNum"), "%d", pPageFrame->GetVirtPageNum()); + OUString aFormatName = pPageFrame->GetPageDesc()->GetName(); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("pageDesc"), "%s", BAD_CAST(OUStringToOString(aFormatName, RTL_TEXTENCODING_UTF8).getStr())); + xmlTextWriterEndElement(writer); +#ifdef UNX + // disable for tests to avoid resolving loads of merge conflicts (var is only set on UNX in this branch) + if (!getenv("LO_TESTNAME")) if (auto const* pObjs = pPageFrame->GetSortedObjs()) + { + (void)xmlTextWriterStartElement(writer, BAD_CAST("sorted_objs")); + for (SwAnchoredObject const*const pObj : *pObjs) + { // just print pointer, full details will be printed on its anchor frame + // this nonsense is needed because of multiple inheritance + if (SwFlyFrame const*const pFly = dynamic_cast<SwFlyFrame const*>(pObj)) + { + (void)xmlTextWriterStartElement(writer, BAD_CAST("fly")); + (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", pFly); + } + else + { + (void)xmlTextWriterStartElement(writer, BAD_CAST(pObj->getElementName())); + (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", pObj); + } + (void)xmlTextWriterEndElement(writer); + } + (void)xmlTextWriterEndElement(writer); + } +#endif } if (IsTextFrame()) @@ -421,22 +452,16 @@ void SwFrame::dumpInfosAsXml( xmlTextWriterPtr writer ) const { // output the Frame xmlTextWriterStartElement( writer, BAD_CAST( "bounds" ) ); - xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "left" ), "%ld", getFrameArea().Left() ); - xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "top" ), "%ld", getFrameArea().Top() ); - xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "width" ), "%ld", getFrameArea().Width() ); - xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "height" ), "%ld", getFrameArea().Height() ); + getFrameArea().dumpAsXmlAttributes(writer); xmlTextWriterWriteAttribute(writer, BAD_CAST("mbFixSize"), BAD_CAST(OString::boolean(HasFixSize()).getStr())); - xmlTextWriterWriteAttribute(writer, BAD_CAST("mbValidPos"), BAD_CAST(OString::boolean(isFrameAreaPositionValid()).getStr())); - xmlTextWriterWriteAttribute(writer, BAD_CAST("mbValidSize"), BAD_CAST(OString::boolean(isFrameAreaSizeValid()).getStr())); - xmlTextWriterWriteAttribute(writer, BAD_CAST("mbValidPrtArea"), BAD_CAST(OString::boolean(isFramePrintAreaValid()).getStr())); + xmlTextWriterWriteAttribute(writer, BAD_CAST("mbFrameAreaPositionValid"), BAD_CAST(OString::boolean(isFrameAreaPositionValid()).getStr())); + xmlTextWriterWriteAttribute(writer, BAD_CAST("mbFrameAreaSizeValid"), BAD_CAST(OString::boolean(isFrameAreaSizeValid()).getStr())); + xmlTextWriterWriteAttribute(writer, BAD_CAST("mbFramePrintAreaValid"), BAD_CAST(OString::boolean(isFramePrintAreaValid()).getStr())); xmlTextWriterEndElement( writer ); - // output the Prt + // output the print area xmlTextWriterStartElement( writer, BAD_CAST( "prtBounds" ) ); - xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "left" ), "%ld", getFramePrintArea().Left() ); - xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "top" ), "%ld", getFramePrintArea().Top() ); - xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "width" ), "%ld", getFramePrintArea().Width() ); - xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "height" ), "%ld", getFramePrintArea().Height() ); + getFramePrintArea().dumpAsXmlAttributes(writer); xmlTextWriterEndElement( writer ); } @@ -466,6 +491,12 @@ void SwFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const if (pFF->GetFollow()) xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("follow"), "%" SAL_PRIuUINT32, pFF->GetFollow()->GetFrameId() ); } + if (IsSctFrame()) + { + SwSectionFrame const*const pFrame(static_cast<SwSectionFrame const*>(this)); + SwSectionNode const*const pNode(pFrame->GetSection() ? pFrame->GetSection()->GetFormat()->GetSectionNode() : nullptr); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("sectionNodeIndex"), TMP_FORMAT, pNode ? pNode->GetIndex() : -1); + } if ( IsTextFrame( ) ) { const SwTextFrame *pTextFrame = static_cast<const SwTextFrame *>(this); @@ -515,10 +546,8 @@ void SwAnchoredObject::dumpAsXml( xmlTextWriterPtr writer ) const xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "ptr" ), "%p", this ); xmlTextWriterStartElement( writer, BAD_CAST( "bounds" ) ); - xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "left" ), "%ld", GetObjBoundRect().Left() ); - xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "top" ), "%ld", GetObjBoundRect().Top() ); - xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "width" ), "%ld", GetObjBoundRect().Width() ); - xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "height" ), "%ld", GetObjBoundRect().Height() ); + // don't call GetObjBoundRect(), it modifies the layout + SwRect(GetDrawObj()->GetLastBoundRect()).dumpAsXmlAttributes(writer); xmlTextWriterEndElement( writer ); if (const SdrObject* pObject = GetDrawObj()) diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx index eb77942b6fe7..ce533a220bd9 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> @@ -666,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); @@ -894,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! } } @@ -1007,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." ); @@ -1509,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()) @@ -1638,7 +1674,7 @@ lcl_GetTextAttrs( SwTextAttr **const ppTextAttr, SwpHints const *const pSwpHints, sal_Int32 const nIndex, sal_uInt16 const nWhich, - enum SwTextNode::GetTextAttrMode const eMode) + ::sw::GetTextAttrMode const eMode) { assert(nWhich >= RES_TXTATR_BEGIN && nWhich < RES_TXTATR_END); if (!pSwpHints) @@ -1648,9 +1684,12 @@ lcl_GetTextAttrs( bool (*pMatchFunc)(sal_Int32, sal_Int32, sal_Int32)=nullptr; switch (eMode) { - case SwTextNode::DEFAULT: pMatchFunc = &lcl_GetTextAttrDefault; break; - case SwTextNode::EXPAND: pMatchFunc = &lcl_GetTextAttrExpand; break; - case SwTextNode::PARENT: pMatchFunc = &lcl_GetTextAttrParent; break; + case ::sw::GetTextAttrMode::Default: pMatchFunc = &lcl_GetTextAttrDefault; + break; + case ::sw::GetTextAttrMode::Expand: pMatchFunc = &lcl_GetTextAttrExpand; + break; + case ::sw::GetTextAttrMode::Parent: pMatchFunc = &lcl_GetTextAttrParent; + break; default: assert(false); } @@ -1700,13 +1739,13 @@ SwTextNode::GetTextAttrsAt(sal_Int32 const nIndex, sal_uInt16 const nWhich) cons { assert(nWhich >= RES_TXTATR_BEGIN && nWhich < RES_TXTATR_END); std::vector<SwTextAttr *> ret; - lcl_GetTextAttrs(&ret, nullptr, m_pSwpHints.get(), nIndex, nWhich, DEFAULT); + lcl_GetTextAttrs(&ret, nullptr, m_pSwpHints.get(), nIndex, nWhich, ::sw::GetTextAttrMode::Default); return ret; } SwTextAttr * SwTextNode::GetTextAttrAt(sal_Int32 const nIndex, sal_uInt16 const nWhich, - enum GetTextAttrMode const eMode) const + ::sw::GetTextAttrMode const eMode) const { assert( (nWhich == RES_TXTATR_META) || (nWhich == RES_TXTATR_METAFIELD) @@ -1724,11 +1763,11 @@ SwTextNode::GetTextAttrAt(sal_Int32 const nIndex, sal_uInt16 const nWhich, const SwTextInputField* SwTextNode::GetOverlappingInputField( const SwTextAttr& rTextAttr ) const { - const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt( rTextAttr.GetStart(), RES_TXTATR_INPUTFIELD, PARENT )); + const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt(rTextAttr.GetStart(), RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent)); if ( pTextInputField == nullptr && rTextAttr.End() != nullptr ) { - pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt( *(rTextAttr.End()), RES_TXTATR_INPUTFIELD, PARENT )); + pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt(*(rTextAttr.End()), RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent)); } return pTextInputField; @@ -1751,7 +1790,7 @@ void SwTextNode::DelFrames_TextNodePart() SwTextField* SwTextNode::GetFieldTextAttrAt( const sal_Int32 nIndex, - const bool bIncludeInputFieldAtStart ) const + ::sw::GetTextAttrMode const eMode) const { SwTextField* pTextField = dynamic_cast<SwTextField*>(GetTextAttrForCharAt( nIndex, RES_TXTATR_FIELD )); if ( pTextField == nullptr ) @@ -1764,7 +1803,7 @@ SwTextField* SwTextNode::GetFieldTextAttrAt( dynamic_cast<SwTextField*>( GetTextAttrAt( nIndex, RES_TXTATR_INPUTFIELD, - bIncludeInputFieldAtStart ? DEFAULT : EXPAND )); + eMode)); } return pTextField; @@ -2492,7 +2531,15 @@ void SwTextNode::CutImpl( SwTextNode * const pDest, const SwIndex & rDestStart, } else { - GetpSwAttrSet()->CopyToModify( *pDest ); + // Copy all attrs except RES_PARATR_LIST_LEVEL: it was initialized before + // and current SwTextNode can contain not suitable for pDest value + SfxItemSet aCharSet( + pDest->GetDoc()->GetAttrPool(), + svl::Items<RES_CHRATR_BEGIN, RES_PARATR_LIST_LEVEL - 1, + RES_PARATR_LIST_LEVEL + 1, HINT_END>{}); + aCharSet.Put(*GetpSwAttrSet()); + if (aCharSet.Count()) + pDest->SetAttr(aCharSet, nDestStart, nDestStart + nLen); } } @@ -3072,11 +3119,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()))); diff --git a/sw/source/core/txtnode/thints.cxx b/sw/source/core/txtnode/thints.cxx index 5b363f2d9119..ba2889236f52 100644 --- a/sw/source/core/txtnode/thints.cxx +++ b/sw/source/core/txtnode/thints.cxx @@ -1180,6 +1180,12 @@ void SwTextNode::DestroyAttr( SwTextAttr* pAttr ) SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pAttr)); SwFieldType* pFieldType = pAttr->GetFormatField().GetField()->GetTyp(); + if (SwFieldIds::Dde != pFieldType->Which() + && !pTextField->GetpTextNode()) + { + break; // was not yet inserted + } + //JP 06-08-95: DDE-fields are an exception assert(SwFieldIds::Dde == pFieldType->Which() || this == pTextField->GetpTextNode()); @@ -1599,6 +1605,7 @@ bool SwTextNode::InsertHint( SwTextAttr * const pAttr, const SetAttrMode nMode ) if ( pAttr->End() == nullptr ) { bInsertHint = false; + DestroyAttr(pAttr); } else { @@ -2971,6 +2978,7 @@ bool SwpHints::TryInsertHint( if ( MAX_HINTS <= Count() ) // we're sorry, this flight is overbooked... { OSL_FAIL("hints array full :-("); + rNode.DestroyAttr(pHint); return false; } diff --git a/sw/source/core/txtnode/txatbase.cxx b/sw/source/core/txtnode/txatbase.cxx index 188ec6f9a663..755391e940d7 100644 --- a/sw/source/core/txtnode/txatbase.cxx +++ b/sw/source/core/txtnode/txatbase.cxx @@ -152,9 +152,11 @@ void SwTextAttr::dumpAsXml(xmlTextWriterPtr pWriter) const GetAutoFormat().dumpAsXml(pWriter); break; case RES_TXTATR_FIELD: + case RES_TXTATR_INPUTFIELD: GetFormatField().dumpAsXml(pWriter); break; default: + SAL_WARN("sw.core", "Unhandled TXTATR"); break; } diff --git a/sw/source/core/txtnode/txtedt.cxx b/sw/source/core/txtnode/txtedt.cxx index 5c599d78afab..73cb47930eac 100644 --- a/sw/source/core/txtnode/txtedt.cxx +++ b/sw/source/core/txtnode/txtedt.cxx @@ -375,10 +375,10 @@ void SwTextNode::RstTextAttr( sal_Int32 nEnd = nStt + nLen; { // enlarge range for the reset of text attributes in case of an overlapping input field - const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt( nStt, RES_TXTATR_INPUTFIELD, PARENT )); + const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt(nStt, RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent)); if ( pTextInputField == nullptr ) { - pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt(nEnd, RES_TXTATR_INPUTFIELD, PARENT )); + pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt(nEnd, RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent)); } if ( pTextInputField != nullptr ) { diff --git a/sw/source/core/undo/unbkmk.cxx b/sw/source/core/undo/unbkmk.cxx index 6cc805d1518d..e9d41a7bcd3e 100644 --- a/sw/source/core/undo/unbkmk.cxx +++ b/sw/source/core/undo/unbkmk.cxx @@ -56,7 +56,7 @@ void SwUndoBookmark::ResetInDoc( SwDoc* pDoc ) { if ( m_pHistoryBookmark->IsEqualBookmark( **ppBkmk ) ) { - pMarkAccess->deleteMark( ppBkmk ); + pMarkAccess->deleteMark(ppBkmk, false); break; } } diff --git a/sw/source/core/undo/undel.cxx b/sw/source/core/undo/undel.cxx index 4e55a46196b4..cb819d5216c8 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 { @@ -1135,7 +1140,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 +1205,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 +1225,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 +1304,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 fa86072a3008..d611cb4a496b 100644 --- a/sw/source/core/undo/undobj.cxx +++ b/sw/source/core/undo/undobj.cxx @@ -981,10 +981,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()) { @@ -1168,7 +1172,7 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark, && ( bSaveOtherPos || !pBkmk->IsExpanded() ) ) { - pMarkAccess->deleteMark(pMarkAccess->getAllMarksBegin()+n); + pMarkAccess->deleteMark(pMarkAccess->getAllMarksBegin()+n, false); n--; } } @@ -1558,9 +1562,14 @@ static bool IsNotBackspaceHeuristic( SwPosition const& rStart, SwPosition const& rEnd) { // check if the selection is backspace/delete created by DelLeft/DelRight - return rStart.nNode.GetIndex() + 1 != rEnd.nNode.GetIndex() - || rEnd.nContent != 0 - || rStart.nContent != rStart.nNode.GetNode().GetTextNode()->Len(); + if (rStart.nNode.GetIndex() + 1 != rEnd.nNode.GetIndex()) + return true; + if (rEnd.nContent != 0) + return true; + const SwTextNode* pTextNode = rStart.nNode.GetNode().GetTextNode(); + if (!pTextNode || rStart.nContent != pTextNode->Len()) + return true; + return false; } bool IsDestroyFrameAnchoredAtChar(SwPosition const & rAnchorPos, diff --git a/sw/source/core/undo/unins.cxx b/sw/source/core/undo/unins.cxx index 85b20fae911b..f1f7f48188aa 100644 --- a/sw/source/core/undo/unins.cxx +++ b/sw/source/core/undo/unins.cxx @@ -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..c7dbc781225b 100644 --- a/sw/source/core/undo/untbl.cxx +++ b/sw/source/core/undo/untbl.cxx @@ -2420,11 +2420,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 +2501,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); @@ -2582,7 +2584,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>( @@ -2682,7 +2684,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 +2756,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/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/unochart.cxx b/sw/source/core/unocore/unochart.cxx index 38241c451a67..bb43665ec6a2 100644 --- a/sw/source/core/unocore/unochart.cxx +++ b/sw/source/core/unocore/unochart.cxx @@ -1427,7 +1427,7 @@ void SwChartDataProvider::RemoveDataSequence( const SwTable &rTable, uno::Refere aDataSequences[ &rTable ].erase( rxDataSequence ); } -void SwChartDataProvider::InvalidateTable( const SwTable *pTable ) +void SwChartDataProvider::InvalidateTable( const SwTable *pTable, bool bImmediate ) { OSL_ENSURE( pTable, "table pointer is NULL" ); if (pTable) @@ -1447,6 +1447,10 @@ void SwChartDataProvider::InvalidateTable( const SwTable *pTable ) } } } + + // tdf#122995 added Immediate-mode to allow non-timer-delayed Chart invalidation + if (bImmediate && !bDisposed) + pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().Disconnect(); } void SwChartDataProvider::DeleteBox( const SwTable *pTable, const SwTableBox &rBox ) diff --git a/sw/source/core/unocore/unocrsrhelper.cxx b/sw/source/core/unocore/unocrsrhelper.cxx index d7634e2a2713..9d0710cf3384 100644 --- a/sw/source/core/unocore/unocrsrhelper.cxx +++ b/sw/source/core/unocore/unocrsrhelper.cxx @@ -283,8 +283,8 @@ GetNestedTextContent(SwTextNode const & rTextNode, sal_Int32 const nIndex, bool const bParent) { // these should be unambiguous because of the dummy character - SwTextNode::GetTextAttrMode const eMode( bParent - ? SwTextNode::PARENT : SwTextNode::EXPAND ); + auto const eMode( bParent + ? ::sw::GetTextAttrMode::Parent : ::sw::GetTextAttrMode::Expand ); SwTextAttr *const pMetaTextAttr = rTextNode.GetTextAttrAt(nIndex, RES_TXTATR_META, eMode); SwTextAttr *const pMetaFieldTextAttr = @@ -582,7 +582,7 @@ bool getCursorPropertyValue(const SfxItemPropertySimpleEntry& rEntry const SwTextNode *pTextNd = rPam.GetDoc()->GetNodes()[pPos->nNode.GetIndex()]->GetTextNode(); const SwTextAttr* pTextAttr = pTextNd - ? pTextNd->GetFieldTextAttrAt( pPos->nContent.GetIndex(), true ) + ? pTextNd->GetFieldTextAttrAt(pPos->nContent.GetIndex(), ::sw::GetTextAttrMode::Default) : nullptr; if ( pTextAttr != nullptr ) { diff --git a/sw/source/core/unocore/unodraw.cxx b/sw/source/core/unocore/unodraw.cxx index fed0e4a55d6a..1d7cc5fea594 100644 --- a/sw/source/core/unocore/unodraw.cxx +++ b/sw/source/core/unocore/unodraw.cxx @@ -1147,6 +1147,8 @@ void SwXShape::setPropertyValue(const OUString& rPropertyName, const uno::Any& a SwFormatFlyCnt aFormat( pFormat ); pNd->InsertItem(aFormat, pInternalPam->GetPoint() ->nContent.GetIndex(), 0 ); + //Refetch in case SwTextNode::InsertItem causes it to be deleted + pFormat = GetFrameFormat(); } else { diff --git a/sw/source/core/unocore/unofield.cxx b/sw/source/core/unocore/unofield.cxx index b5b7f4e1a88e..d5061ea58658 100644 --- a/sw/source/core/unocore/unofield.cxx +++ b/sw/source/core/unocore/unofield.cxx @@ -1327,7 +1327,7 @@ void SwXTextField::TransmuteLeadToInputField(SwSetExpField & rField) bool bSuccess = rIDCO.InsertPoolItem(*pPamForTextField, tempFormat); assert(bSuccess); (void) bSuccess; - SwTextField const* pNewAttr(rNode.GetFieldTextAttrAt(nStart, true)); + SwTextField const* pNewAttr(rNode.GetFieldTextAttrAt(nStart, ::sw::GetTextAttrMode::Default)); assert(pNewAttr); SwFormatField const& rNewFormat(pNewAttr->GetFormatField()); assert(rNewFormat.Which() == (static_cast<SwSetExpField const*>(rNewFormat.GetField())->GetInputFlag() ? RES_TXTATR_INPUTFIELD : RES_TXTATR_FIELD)); @@ -2011,7 +2011,7 @@ void SAL_CALL SwXTextField::attach( else pDoc->getIDocumentContentOperations().InsertPoolItem(aPam, aFormat, nInsertFlags); - SwTextAttr* pTextAttr = aPam.GetNode().GetTextNode()->GetFieldTextAttrAt( aPam.GetPoint()->nContent.GetIndex()-1, true ); + SwTextAttr* pTextAttr = aPam.GetNode().GetTextNode()->GetFieldTextAttrAt(aPam.GetPoint()->nContent.GetIndex()-1, ::sw::GetTextAttrMode::Default); // What about updating the fields? (see fldmgr.cxx) if (!pTextAttr) @@ -2075,7 +2075,7 @@ void SAL_CALL SwXTextField::attach( } // keep inserted annotation { - SwTextField* pTextAttr = aEnd.GetNode().GetTextNode()->GetFieldTextAttrAt( aEnd.End()->nContent.GetIndex()-1, true ); + SwTextField *const pTextAttr = aEnd.GetNode().GetTextNode()->GetFieldTextAttrAt(aEnd.End()->nContent.GetIndex()-1, ::sw::GetTextAttrMode::Default); if ( pTextAttr != nullptr ) { m_pImpl->SetFormatField(const_cast<SwFormatField*>(&pTextAttr->GetFormatField()), m_pImpl->m_pDoc); 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/unomap.cxx b/sw/source/core/unocore/unomap.cxx index 05695e8585b2..63fbd6e78c13 100644 --- a/sw/source/core/unocore/unomap.cxx +++ b/sw/source/core/unocore/unomap.cxx @@ -952,6 +952,7 @@ const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetPropertyMapEntries(s {OUString(UNO_NAME_HINT), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, {OUString(UNO_NAME_HELP), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, {OUString(UNO_NAME_TOOLTIP), FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_MISC_OBJ_INTEROPGRABBAG), FIELD_PROP_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0}, COMMON_FLDTYP_PROPERTIES { OUString(), 0, css::uno::Type(), 0, 0 } }; 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/unotext.cxx b/sw/source/core/unocore/unotext.cxx index 3887a11191d7..a4dfe5422af1 100644 --- a/sw/source/core/unocore/unotext.cxx +++ b/sw/source/core/unocore/unotext.cxx @@ -1566,6 +1566,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 +1648,7 @@ SwXText::convertToTextFrame( bParaAfterInserted = GetDoc()->getIDocumentContentOperations().AppendTextNode( aEnd ); pEndPam->DeleteMark(); *pEndPam->GetPoint() = aEnd; + *oAnchorCheckPam->End() = aEnd; } pStartPam->SetMark(); *pStartPam->End() = *pEndPam->End(); @@ -1660,10 +1663,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 +1685,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 +1703,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()); diff --git a/sw/source/core/view/printdata.cxx b/sw/source/core/view/printdata.cxx index a2da5005cc38..a92f3661c990 100644 --- a/sw/source/core/view/printdata.cxx +++ b/sw/source/core/view/printdata.cxx @@ -301,8 +301,7 @@ SwPrintUIOptions::SwPrintUIOptions( aWidgetIds[4] = "rbRangeSelection"; m_aUIProperties[nIdx++].Value = setChoiceRadiosControlOpt(aWidgetIds, OUString(), aHelpIds, aPrintRangeName, - aChoices, - bHasSelection ? 4 : 0, + aChoices, 0 /* always default to 'All pages' */, aChoicesDisabled); // show an Edit dependent on "Pages" selected diff --git a/sw/source/filter/basflt/fltshell.cxx b/sw/source/filter/basflt/fltshell.cxx index 7d96867acb00..8a94cd48d5b3 100644 --- a/sw/source/filter/basflt/fltshell.cxx +++ b/sw/source/filter/basflt/fltshell.cxx @@ -660,7 +660,7 @@ void SwFltControlStack::SetAttrInDoc(const SwPosition& rTmpPos, SwTextNode const*const pTextNode( aRegion.End()->nNode.GetNode().GetTextNode()); SwTextField const*const pField = pTextNode ? pTextNode->GetFieldTextAttrAt( - aRegion.End()->nContent.GetIndex() - 1, true) : nullptr; + aRegion.End()->nContent.GetIndex() - 1, ::sw::GetTextAttrMode::Default) : nullptr; if (pField) { SwPostItField const*const pPostIt( 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 ce689df01b9d..f0edba59c107 100644 --- a/sw/source/filter/html/htmltab.cxx +++ b/sw/source/filter/html/htmltab.cxx @@ -34,6 +34,7 @@ #include <svtools/htmlkywd.hxx> #include <svl/urihelper.hxx> #include <svl/listener.hxx> +#include <svx/sdrobjectuser.hxx> #include <sal/log.hxx> #include <dcontact.hxx> @@ -372,7 +373,7 @@ typedef std::vector<HTMLTableColumn> HTMLTableColumns; typedef std::vector<SdrObject *> SdrObjects; -class HTMLTable +class HTMLTable : public sdr::ObjectUser { OUString m_aId; OUString m_aStyle; @@ -520,6 +521,8 @@ private: sal_uInt16 GetBorderWidth( const SvxBorderLine& rBLine, bool bWithDistance=false ) const; + virtual void ObjectInDestruction(const SdrObject& rObject) override; + public: bool m_bFirstCell; // is there a cell created already? @@ -529,7 +532,7 @@ public: bool bHasToFly, const HTMLTableOptions& rOptions); - ~HTMLTable(); + virtual ~HTMLTable(); // Identifying of a cell const HTMLTableCell& GetCell(sal_uInt16 nRow, sal_uInt16 nCell) const; @@ -968,14 +971,8 @@ void HTMLTable::InitCtor(const HTMLTableOptions& rOptions) m_aRightBorderLine = m_aLeftBorderLine; if( rOptions.nCellSpacing != 0 ) - { m_aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE); - m_aBorderLine.SetWidth( DEF_LINE_WIDTH_0 ); - } - else - { - m_aBorderLine.SetWidth( DEF_LINE_WIDTH_0 ); - } + m_aBorderLine.SetWidth(SvxBorderLineWidth::Hairline); m_aBorderLine.SetColor( rBorderColor ); if( m_nCellPadding ) @@ -1065,11 +1062,33 @@ bool SwHTMLParser::IsReqIF() const return m_bReqIF; } +// if any m_pResizeDrawObjects members are deleted during parse, remove them +// from m_pResizeDrawObjects and m_pDrawObjectPrcWidths +void HTMLTable::ObjectInDestruction(const SdrObject& rObject) +{ + auto it = std::find(m_pResizeDrawObjects->begin(), m_pResizeDrawObjects->end(), &rObject); + assert(it != m_pResizeDrawObjects->end()); + auto nIndex = std::distance(m_pResizeDrawObjects->begin(), it); + m_pResizeDrawObjects->erase(it); + auto otherit = m_pDrawObjectPrcWidths->begin() + nIndex * 3; + m_pDrawObjectPrcWidths->erase(otherit, otherit + 3); +} + HTMLTable::~HTMLTable() { m_pParser->DeregisterHTMLTable(this); - m_pResizeDrawObjects.reset(); + if (m_pResizeDrawObjects) + { + size_t nCount = m_pResizeDrawObjects->size(); + for (size_t i = 0; i < nCount; ++i) + { + SdrObject *pObj = (*m_pResizeDrawObjects)[i]; + pObj->RemoveObjectUser(*this); + } + m_pResizeDrawObjects.reset(); + } + m_pDrawObjectPrcWidths.reset(); m_pContext.reset(); @@ -2483,6 +2502,7 @@ void HTMLTable::RegisterDrawObject( SdrObject *pObj, sal_uInt8 nPrcWidth ) if( !m_pResizeDrawObjects ) m_pResizeDrawObjects.reset(new SdrObjects); m_pResizeDrawObjects->push_back( pObj ); + pObj->AddObjectUser(*this); if( !m_pDrawObjectPrcWidths ) m_pDrawObjectPrcWidths.reset(new std::vector<sal_uInt16>); diff --git a/sw/source/filter/html/htmltabw.cxx b/sw/source/filter/html/htmltabw.cxx index 577a9f5c7b11..18ad069e605b 100644 --- a/sw/source/filter/html/htmltabw.cxx +++ b/sw/source/filter/html/htmltabw.cxx @@ -811,9 +811,9 @@ void SwHTMLWrtTable::Write( SwHTMLWriter& rWrt, sal_Int16 eAlign, OutTableCells( rWrt, pRow2->GetCells(), pRow2->GetBackground() ); if( !m_nCellSpacing && nRow < m_aRows.size()-1 && pRow2->bBottomBorder && - pRow2->nBottomBorder > DEF_LINE_WIDTH_1 ) + pRow2->nBottomBorder > SvxBorderLineWidth::Hairline ) { - for( auto nCnt = (pRow2->nBottomBorder / DEF_LINE_WIDTH_1) - 1; nCnt; --nCnt ) + for( auto nCnt = (pRow2->nBottomBorder / SvxBorderLineWidth::Hairline) - 1; nCnt; --nCnt ) { rWrt.OutNewLine(); HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow ); diff --git a/sw/source/filter/html/svxcss1.cxx b/sw/source/filter/html/svxcss1.cxx index c1a6f7312d30..e570d1a92c54 100644 --- a/sw/source/filter/html/svxcss1.cxx +++ b/sw/source/filter/html/svxcss1.cxx @@ -244,9 +244,9 @@ static CSS1PropertyEnum const aBulletStyleTable[] = static sal_uInt16 const aBorderWidths[] = { - DEF_LINE_WIDTH_0, - DEF_LINE_WIDTH_5, - DEF_LINE_WIDTH_1 + SvxBorderLineWidth::Hairline, + SvxBorderLineWidth::VeryThin, + SvxBorderLineWidth::Thin }; #undef SBORDER_ENTRY diff --git a/sw/source/filter/html/swhtml.cxx b/sw/source/filter/html/swhtml.cxx index 3f72a8a044e3..1a08be28684a 100644 --- a/sw/source/filter/html/swhtml.cxx +++ b/sw/source/filter/html/swhtml.cxx @@ -5305,12 +5305,12 @@ void SwHTMLParser::InsertHorzRule() } else if( bNoShade ) { - aBorderLine.SetWidth( DEF_LINE_WIDTH_2 ); + aBorderLine.SetWidth( SvxBorderLineWidth::Medium ); } else { aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE); - aBorderLine.SetWidth( DEF_LINE_WIDTH_0 ); + aBorderLine.SetWidth(SvxBorderLineWidth::Hairline); } SvxBoxItem aBoxItem(RES_BOX); diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 5dbb02ce9eb5..f45de340f91d 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -301,6 +301,28 @@ static bool lcl_isOnelinerSdt(const OUString& rName) return rName == "Title" || rName == "Subtitle" || rName == "Company"; } +static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrs, ...) +{ + if (!pAttrList.is()) + pAttrList = FastSerializerHelper::createAttrList(); + + va_list args; + va_start(args, nAttrs); + for (sal_Int32 i = 0; i < nAttrs; i++) + { + sal_Int32 nName = va_arg(args, sal_Int32); + const char* pValue = va_arg(args, const char*); + if (pValue) + pAttrList->add(nName, pValue); + } + va_end(args); +} + +static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrName, const char* sAttrValue) +{ + AddToAttrList(pAttrList, 1, nAttrName, sAttrValue); +} + // write a floating table directly to docx without the surrounding frame void DocxAttributeOutput::WriteFloatingTable(ww8::Frame const* pParentFrame) { @@ -438,7 +460,7 @@ void DocxAttributeOutput::StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pText // would normally arrive, it would be too late (would be after the // paragraph start has been written). bool bEndParaSdt = false; - if (m_bStartedParaSdt) + if (m_aParagraphSdt.m_bStartedSdt) { SwTextNode* pTextNode = m_rExport.m_pCurPam->GetNode().GetTextNode(); if (pTextNode && pTextNode->GetpSwAttrSet()) @@ -448,17 +470,16 @@ void DocxAttributeOutput::StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pText { const SfxGrabBagItem& rParaGrabBag = static_cast<const SfxGrabBagItem&>(*pItem); const std::map<OUString, css::uno::Any>& rMap = rParaGrabBag.GetGrabBag(); - bEndParaSdt = m_bStartedParaSdt && rMap.find("ParaSdtEndBefore") != rMap.end(); + bEndParaSdt = m_aParagraphSdt.m_bStartedSdt && rMap.find("ParaSdtEndBefore") != rMap.end(); } } } // TODO also avoid multiline paragraphs in those SDT types for shape text - bool bOneliner = m_bStartedParaSdt && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() && lcl_isOnelinerSdt(m_aStartedParagraphSdtPrAlias); - if (bEndParaSdt || (m_bStartedParaSdt && m_bHadSectPr) || bOneliner) + bool bOneliner = m_aParagraphSdt.m_bStartedSdt && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() && lcl_isOnelinerSdt(m_aStartedParagraphSdtPrAlias); + if (bEndParaSdt || (m_aParagraphSdt.m_bStartedSdt && m_bHadSectPr) || bOneliner) { // This is the common case: "close sdt before the current paragraph" was requested by the next paragraph. - EndSdtBlock(); - m_bStartedParaSdt = false; + m_aParagraphSdt.EndSdtBlock(m_pSerializer); m_aStartedParagraphSdtPrAlias.clear(); } m_bHadSectPr = false; @@ -551,14 +572,270 @@ static OString convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel) } } -static void lcl_deleteAndResetTheLists( rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenChildren, rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrDataBindingAttrs, OUString& rSdtPrAlias) +void SdtBlockHelper::DeleteAndResetTheLists() +{ + if (m_pTokenChildren.is() ) + m_pTokenChildren.clear(); + if (m_pDataBindingAttrs.is() ) + m_pDataBindingAttrs.clear(); + if (m_pTextAttrs.is()) + m_pTextAttrs.clear(); + if (!m_aAlias.isEmpty()) + m_aAlias.clear(); + if (!m_aPlaceHolderDocPart.isEmpty()) + m_aPlaceHolderDocPart.clear(); + if (!m_aColor.isEmpty()) + m_aColor.clear(); + m_bHasId = false; +} + +void SdtBlockHelper::WriteSdtBlock(::sax_fastparser::FSHelperPtr& pSerializer, bool bRunTextIsOn, bool bParagraphHasDrawing) +{ + if (m_nSdtPrToken <= 0 && !m_pDataBindingAttrs.is() && !m_bHasId) + return; + + // sdt start mark + pSerializer->mark(Tag_WriteSdtBlock); + + pSerializer->startElementNS(XML_w, XML_sdt); + + // output sdt properties + pSerializer->startElementNS(XML_w, XML_sdtPr); + + if (m_nSdtPrToken > 0 && m_pTokenChildren.is()) + { + if (!m_pTokenAttributes.is()) + pSerializer->startElement(m_nSdtPrToken); + else + { + XFastAttributeListRef xAttrList( m_pTokenAttributes.get() ); + m_pTokenAttributes.clear(); + pSerializer->startElement(m_nSdtPrToken, xAttrList); + } + + if (m_nSdtPrToken == FSNS(XML_w, XML_date) || m_nSdtPrToken == FSNS(XML_w, XML_docPartObj) || m_nSdtPrToken == FSNS(XML_w, XML_docPartList) || m_nSdtPrToken == FSNS(XML_w14, XML_checkbox)) { + const uno::Sequence<xml::FastAttribute> aChildren = m_pTokenChildren->getFastAttributes(); + for (const auto& rChild : aChildren) + pSerializer->singleElement(rChild.Token, FSNS(XML_w, XML_val), rChild.Value.toUtf8() ); + } + + pSerializer->endElement(m_nSdtPrToken); + } + else if ((m_nSdtPrToken > 0) && m_nSdtPrToken != FSNS(XML_w, XML_id) && !(bRunTextIsOn && bParagraphHasDrawing)) + { + if (!m_pTokenAttributes.is()) + pSerializer->singleElement(m_nSdtPrToken); + else + { + XFastAttributeListRef xAttrList( m_pTokenAttributes.get() ); + m_pTokenAttributes.clear(); + pSerializer->singleElement(m_nSdtPrToken, xAttrList); + } + } + + WriteExtraParams(pSerializer); + + pSerializer->endElementNS(XML_w, XML_sdtPr); + + // sdt contents start tag + pSerializer->startElementNS(XML_w, XML_sdtContent); + + // prepend the tags since the sdt start mark before the paragraph + pSerializer->mergeTopMarks(Tag_WriteSdtBlock, sax_fastparser::MergeMarks::PREPEND); + + // write the ending tags after the paragraph + m_bStartedSdt = true; + + // clear sdt status + m_nSdtPrToken = 0; + m_pTokenChildren.clear(); + m_pDataBindingAttrs.clear(); + m_pTextAttrs.clear(); + m_aAlias.clear(); + m_bHasId = false; +} + +void SdtBlockHelper::WriteExtraParams(::sax_fastparser::FSHelperPtr& pSerializer) +{ + if (m_nSdtPrToken == FSNS(XML_w, XML_id) || m_bHasId) + //Word won't open a document with an empty id tag, we fill it with a random number + pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val), + OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max()))); + + if (m_pDataBindingAttrs.is()) + { + XFastAttributeListRef xAttrList( m_pDataBindingAttrs.get() ); + m_pDataBindingAttrs.clear(); + pSerializer->singleElementNS(XML_w, XML_dataBinding, xAttrList); + } + + if (m_pTextAttrs.is()) + { + XFastAttributeListRef xAttrList( m_pTextAttrs.get() ); + m_pTextAttrs.clear(); + pSerializer->singleElementNS(XML_w, XML_text, xAttrList); + } + + if (!m_aPlaceHolderDocPart.isEmpty()) + { + pSerializer->startElementNS(XML_w, XML_placeholder); + pSerializer->singleElementNS(XML_w, XML_docPart, FSNS(XML_w, XML_val), m_aPlaceHolderDocPart.toUtf8() ); + pSerializer->endElementNS(XML_w, XML_placeholder); + } + if (!m_aColor.isEmpty()) + { + pSerializer->singleElementNS(XML_w15, XML_color, FSNS(XML_w, XML_val), m_aColor.toUtf8() ); + } + + if (!m_aAlias.isEmpty()) + pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), m_aAlias.toUtf8() ); +} + +void SdtBlockHelper::EndSdtBlock(::sax_fastparser::FSHelperPtr& pSerializer) { - if( pSdtPrTokenChildren.is() ) - pSdtPrTokenChildren.clear(); - if( pSdtPrDataBindingAttrs.is() ) - pSdtPrDataBindingAttrs.clear(); - if (!rSdtPrAlias.isEmpty()) - rSdtPrAlias.clear(); + pSerializer->endElementNS(XML_w, XML_sdtContent); + pSerializer->endElementNS(XML_w, XML_sdt); + m_bStartedSdt = false; +} + +void SdtBlockHelper::GetSdtParamsFromGrabBag(const uno::Sequence<beans::PropertyValue>& aGrabBagSdt) +{ + for (const beans::PropertyValue& aPropertyValue : aGrabBagSdt) + { + if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox") + { + m_nSdtPrToken = FSNS(XML_w14, XML_checkbox); + uno::Sequence<beans::PropertyValue> aGrabBag; + aPropertyValue.Value >>= aGrabBag; + for (const auto& rProp : std::as_const(aGrabBag)) + { + OUString sValue = rProp.Value.get<OUString>(); + if (rProp.Name == "ooxml:CT_SdtCheckbox_checked") + AddToAttrList(m_pTokenChildren, + FSNS(XML_w14, XML_checked), + OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); + else if (rProp.Name == "ooxml:CT_SdtCheckbox_checkedState") + AddToAttrList(m_pTokenChildren, + FSNS(XML_w14, XML_checkedState), + OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); + else if (rProp.Name == "ooxml:CT_SdtCheckbox_uncheckedState") + AddToAttrList(m_pTokenChildren, + FSNS(XML_w14, XML_uncheckedState), + OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); + } + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" && !m_pDataBindingAttrs.is()) + { + uno::Sequence<beans::PropertyValue> aGrabBag; + aPropertyValue.Value >>= aGrabBag; + for (const auto& rProp : std::as_const(aGrabBag)) + { + OUString sValue = rProp.Value.get<OUString>(); + if (rProp.Name == "ooxml:CT_DataBinding_prefixMappings") + AddToAttrList( m_pDataBindingAttrs, + FSNS( XML_w, XML_prefixMappings ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + else if (rProp.Name == "ooxml:CT_DataBinding_xpath") + AddToAttrList( m_pDataBindingAttrs, + FSNS( XML_w, XML_xpath ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + else if (rProp.Name == "ooxml:CT_DataBinding_storeItemID") + AddToAttrList( m_pDataBindingAttrs, + FSNS( XML_w, XML_storeItemID ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text") + { + uno::Sequence<beans::PropertyValue> aGrabBag; + aPropertyValue.Value >>= aGrabBag; + if (aGrabBag.hasElements()) + { + for (const auto& rProp : std::as_const(aGrabBag)) + { + OUString sValue = rProp.Value.get<OUString>(); + if (rProp.Name == "ooxml:CT_SdtText_multiLine") + AddToAttrList(m_pTextAttrs, + FSNS(XML_w, XML_multiLine), + OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); + } + } + else + { + // We still have w:text, but no attrs + m_nSdtPrToken = FSNS(XML_w, XML_text); + } + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPlaceholder_docPart") + { + uno::Sequence<beans::PropertyValue> aGrabBag; + aPropertyValue.Value >>= aGrabBag; + for (const auto& rProp : std::as_const(aGrabBag)) + { + OUString sValue = rProp.Value.get<OUString>(); + if (rProp.Name == "ooxml:CT_SdtPlaceholder_docPart_val") + m_aPlaceHolderDocPart = sValue; + } + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_color") + { + uno::Sequence<beans::PropertyValue> aGrabBag; + aPropertyValue.Value >>= aGrabBag; + for (const auto& rProp : std::as_const(aGrabBag)) + { + OUString sValue = rProp.Value.get<OUString>(); + if (rProp.Name == "ooxml:CT_SdtColor_val") + m_aColor = sValue; + } + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && m_aAlias.isEmpty()) + { + if (!(aPropertyValue.Value >>= m_aAlias)) + SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt alias value"); + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id") + m_bHasId = true; + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation") + m_nSdtPrToken = FSNS(XML_w, XML_citation); + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj" || + aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList") + { + if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj") + m_nSdtPrToken = FSNS(XML_w, XML_docPartObj); + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList") + m_nSdtPrToken = FSNS(XML_w, XML_docPartList); + + uno::Sequence<beans::PropertyValue> aGrabBag; + aPropertyValue.Value >>= aGrabBag; + for (const auto& rProp : std::as_const(aGrabBag)) + { + OUString sValue = rProp.Value.get<OUString>(); + if (rProp.Name == "ooxml:CT_SdtDocPart_docPartGallery") + AddToAttrList(m_pTokenChildren, + FSNS(XML_w, XML_docPartGallery), + OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); + else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartCategory") + AddToAttrList(m_pTokenChildren, + FSNS(XML_w, XML_docPartCategory), + OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); + else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartUnique") + { + if (sValue.isEmpty()) + sValue = "true"; + AddToAttrList(m_pTokenChildren, FSNS(XML_w, XML_docPartUnique), + OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); + } + } + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_equation") + m_nSdtPrToken = FSNS(XML_w, XML_equation); + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_picture") + m_nSdtPrToken = FSNS(XML_w, XML_picture); + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_group") + m_nSdtPrToken = FSNS(XML_w, XML_group); + else + SAL_WARN("sw.ww8", "GetSdtParamsFromGrabBag unhandled SdtPr grab bag property " << aPropertyValue.Name); + } } void DocxAttributeOutput::PopulateFrameProperties(const SwFrameFormat* pFrameFormat, const Size& rSize) @@ -636,7 +913,7 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT ++m_nTextFrameLevel; if( m_nTextFrameLevel == 1 && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() ) { - comphelper::FlagRestorationGuard aStartedParaSdtGuard(m_bStartedParaSdt, false); + comphelper::FlagRestorationGuard aStartedParaSdtGuard(m_aParagraphSdt.m_bStartedSdt, false); assert(!m_pPostponedCustomShape); m_pPostponedCustomShape.reset(new std::vector<PostponedDrawing>); @@ -648,11 +925,10 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT if (!TextBoxIsFramePr(rFrameFormat) || m_bWritingHeaderFooter) { - if (m_bStartedCharSdt) + if (m_aRunSdt.m_bStartedSdt) { // Run-level SDT still open? Close it before AlternateContent. - EndSdtBlock(); - m_bStartedCharSdt = false; + m_aRunSdt.EndSdtBlock(m_pSerializer); } m_pSerializer->startElementNS(XML_w, XML_r); m_pSerializer->startElementNS(XML_mc, XML_AlternateContent); @@ -738,11 +1014,10 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT m_nHyperLinkCount = 0; } - if (m_bStartedCharSdt) + if (m_aRunSdt.m_bStartedSdt) { // Run-level SDT still open? Close it now. - EndSdtBlock(); - m_bStartedCharSdt = false; + m_aRunSdt.EndSdtBlock(m_pSerializer); } if (m_bPageBreakAfter) @@ -754,15 +1029,25 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT m_pSerializer->endElementNS( XML_w, XML_p ); // on export sdt blocks are never nested ATM - if( !m_bAnchorLinkedToNode && !m_bStartedParaSdt ) - WriteSdtBlock( m_nParagraphSdtPrToken, m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrTokenAttributes, m_pParagraphSdtPrDataBindingAttrs, m_aParagraphSdtPrAlias, /*bPara=*/true ); + if (!m_bAnchorLinkedToNode && !m_aParagraphSdt.m_bStartedSdt) + { + m_aParagraphSdt.WriteSdtBlock(m_pSerializer, m_bRunTextIsOn, m_rExport.SdrExporter().IsParagraphHasDrawing()); + + if (m_aParagraphSdt.m_bStartedSdt) + { + if (m_tableReference->m_bTableCellOpen) + m_tableReference->m_bTableCellParaSdtOpen = true; + if (m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen()) + m_rExport.SdrExporter().setParagraphSdtOpen(true); + } + } else { //These should be written out to the actual Node and not to the anchor. //Clear them as they will be repopulated when the node is processed. - m_nParagraphSdtPrToken = 0; - m_bParagraphSdtHasId = false; - lcl_deleteAndResetTheLists( m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrDataBindingAttrs, m_aParagraphSdtPrAlias ); + m_aParagraphSdt.m_nSdtPrToken = 0; + m_aParagraphSdt.m_bHasId = false; + m_aParagraphSdt.DeleteAndResetTheLists(); } //sdtcontent is written so Set m_bParagraphHasDrawing to false @@ -790,107 +1075,6 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT m_aBookmarksOfParagraphEnd.clear(); } -void DocxAttributeOutput::WriteSdtBlock( sal_Int32& nSdtPrToken, - rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenChildren, - rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenAttributes, - rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrDataBindingAttrs, - OUString& rSdtPrAlias, - bool bPara ) -{ - if( nSdtPrToken <= 0 && !pSdtPrDataBindingAttrs.is() ) - return; - - // sdt start mark - m_pSerializer->mark(Tag_WriteSdtBlock); - - m_pSerializer->startElementNS(XML_w, XML_sdt); - - // output sdt properties - m_pSerializer->startElementNS(XML_w, XML_sdtPr); - - if( nSdtPrToken > 0 && pSdtPrTokenChildren.is() ) - { - if (!pSdtPrTokenAttributes.is()) - m_pSerializer->startElement(nSdtPrToken); - else - { - XFastAttributeListRef xAttrList(pSdtPrTokenAttributes.get()); - pSdtPrTokenAttributes.clear(); - m_pSerializer->startElement(nSdtPrToken, xAttrList); - } - - if (nSdtPrToken == FSNS( XML_w, XML_date ) || nSdtPrToken == FSNS( XML_w, XML_docPartObj ) || nSdtPrToken == FSNS( XML_w, XML_docPartList ) || nSdtPrToken == FSNS( XML_w14, XML_checkbox )) { - const uno::Sequence<xml::FastAttribute> aChildren = pSdtPrTokenChildren->getFastAttributes(); - for( const auto& rChild : aChildren ) - m_pSerializer->singleElement( rChild.Token, - FSNS(XML_w, XML_val), rChild.Value.toUtf8() ); - } - - m_pSerializer->endElement( nSdtPrToken ); - } - else if( (nSdtPrToken > 0) && nSdtPrToken != FSNS( XML_w, XML_id ) && !(m_bRunTextIsOn && m_rExport.SdrExporter().IsParagraphHasDrawing())) - { - if (!pSdtPrTokenAttributes.is()) - m_pSerializer->singleElement(nSdtPrToken); - else - { - XFastAttributeListRef xAttrList(pSdtPrTokenAttributes.get()); - pSdtPrTokenAttributes.clear(); - m_pSerializer->singleElement(nSdtPrToken, xAttrList); - } - } - - if( nSdtPrToken == FSNS( XML_w, XML_id ) || ( bPara && m_bParagraphSdtHasId ) ) - //Word won't open a document with an empty id tag, we fill it with a random number - m_pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val), - OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max()))); - - if( pSdtPrDataBindingAttrs.is() && !m_rExport.SdrExporter().IsParagraphHasDrawing()) - { - XFastAttributeListRef xAttrList( pSdtPrDataBindingAttrs.get() ); - pSdtPrDataBindingAttrs.clear(); - m_pSerializer->singleElementNS(XML_w, XML_dataBinding, xAttrList); - } - - if (!rSdtPrAlias.isEmpty()) - m_pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), - rSdtPrAlias.toUtf8()); - - m_pSerializer->endElementNS( XML_w, XML_sdtPr ); - - // sdt contents start tag - m_pSerializer->startElementNS(XML_w, XML_sdtContent); - - // prepend the tags since the sdt start mark before the paragraph - m_pSerializer->mergeTopMarks(Tag_WriteSdtBlock, sax_fastparser::MergeMarks::PREPEND); - - // write the ending tags after the paragraph - if (bPara) - { - m_bStartedParaSdt = true; - if (m_tableReference->m_bTableCellOpen) - m_tableReference->m_bTableCellParaSdtOpen = true; - if (m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen()) - m_rExport.SdrExporter().setParagraphSdtOpen(true); - } - else - // Support multiple runs inside a run-level SDT: don't close the SDT block yet. - m_bStartedCharSdt = true; - - // clear sdt status - nSdtPrToken = 0; - pSdtPrTokenChildren.clear(); - pSdtPrDataBindingAttrs.clear(); - rSdtPrAlias.clear(); - -} - -void DocxAttributeOutput::EndSdtBlock() -{ - m_pSerializer->endElementNS( XML_w, XML_sdtContent ); - m_pSerializer->endElementNS( XML_w, XML_sdt ); -} - #define MAX_CELL_IN_WORD 62 void DocxAttributeOutput::SyncNodelessCells(ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, sal_Int32 nCell, sal_uInt32 nRow) @@ -991,7 +1175,7 @@ void DocxAttributeOutput::SectionBreaks(const SwNode& rNode) if (aNextIndex.GetNode().IsTextNode()) { const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode()); - m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen, pTextNode->GetText().isEmpty()); + m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen); } else if (aNextIndex.GetNode().IsTableNode()) { @@ -1008,7 +1192,7 @@ void DocxAttributeOutput::SectionBreaks(const SwNode& rNode) // Also handle section endings const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode(); if (rNode.StartOfSectionNode()->IsTableNode() || rNode.StartOfSectionNode()->IsSectionNode()) - m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen, pTextNode->GetText().isEmpty()); + m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen); } } } @@ -1326,7 +1510,10 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool / for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin() + nFieldsInPrevHyperlink; pIt != m_Fields.end(); ) { // Add the fields starts for all but hyperlinks and TOCs - if (pIt->bOpen && pIt->pField && pIt->eType != ww::eFORMDROPDOWN) + if (pIt->bOpen && pIt->pField && pIt->eType != ww::eFORMDROPDOWN && + // it is not an input field with extra grabbag params (sdt field) + (!(pIt->eType == ww::eFILLIN && static_cast<const SwInputField*>(pIt->pField.get())->getGrabBagParams().hasElements())) + ) { StartField_Impl( pNode, nPos, *pIt ); @@ -1360,12 +1547,11 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool / // if another sdt starts in this run, then wait // as closing the sdt now, might cause nesting of sdts - if (m_nRunSdtPrToken > 0) + if (m_aRunSdt.m_nSdtPrToken > 0) bCloseEarlierSDT = true; else - EndSdtBlock(); + m_aRunSdt.EndSdtBlock(m_pSerializer); m_bEndCharSdt = false; - m_bStartedCharSdt = false; } if ( m_closeHyperlinkInPreviousRun ) @@ -1391,7 +1577,9 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool / for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin(); pIt != m_Fields.end(); ) { // Add the fields starts for hyperlinks, TOCs and index marks - if (pIt->bOpen && (!pIt->pField || pIt->eType == ww::eFORMDROPDOWN)) + if (pIt->bOpen && (!pIt->pField || pIt->eType == ww::eFORMDROPDOWN || + // InputField with extra grabbag params - it is sdt field + (pIt->eType == ww::eFILLIN && static_cast<const SwInputField*>(pIt->pField.get())->getGrabBagParams().hasElements()))) { StartRedline( m_pRedlineData ); StartField_Impl( pNode, nPos, *pIt, true ); @@ -1523,23 +1711,22 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool / // enclose in a sdt block, if necessary: if one is already started, then don't do it for now // (so on export sdt blocks are never nested ATM) - if ( !m_bAnchorLinkedToNode && !m_bStartedCharSdt ) + if ( !m_bAnchorLinkedToNode && !m_aRunSdt.m_bStartedSdt) { - rtl::Reference<sax_fastparser::FastAttributeList> pRunSdtPrTokenAttributes; - WriteSdtBlock( m_nRunSdtPrToken, m_pRunSdtPrTokenChildren, pRunSdtPrTokenAttributes, m_pRunSdtPrDataBindingAttrs, m_aRunSdtPrAlias, /*bPara=*/false ); + m_aRunSdt.WriteSdtBlock(m_pSerializer, m_bRunTextIsOn, m_rExport.SdrExporter().IsParagraphHasDrawing()); } else { //These should be written out to the actual Node and not to the anchor. //Clear them as they will be repopulated when the node is processed. - m_nRunSdtPrToken = 0; - lcl_deleteAndResetTheLists( m_pRunSdtPrTokenChildren, m_pRunSdtPrDataBindingAttrs, m_aRunSdtPrAlias ); + m_aRunSdt.m_nSdtPrToken = 0; + m_aRunSdt.DeleteAndResetTheLists(); } if (bCloseEarlierSDT) { m_pSerializer->mark(Tag_EndRun_2); - EndSdtBlock(); + m_aRunSdt.EndSdtBlock(m_pSerializer); m_pSerializer->mergeTopMarks(Tag_EndRun_2, sax_fastparser::MergeMarks::PREPEND); } @@ -1898,7 +2085,7 @@ void DocxAttributeOutput::WriteFFData( const FieldInfos& rInfos ) } } -void DocxAttributeOutput::WriteFormDateStart(const OUString& sFullDate, const OUString& sDateFormat, const OUString& sLang) +void DocxAttributeOutput::WriteFormDateStart(const OUString& sFullDate, const OUString& sDateFormat, const OUString& sLang, const uno::Sequence<beans::PropertyValue>& aGrabBagSdt) { m_pSerializer->startElementNS(XML_w, XML_sdt); m_pSerializer->startElementNS(XML_w, XML_sdtPr); @@ -1919,8 +2106,64 @@ void DocxAttributeOutput::WriteFormDateStart(const OUString& sFullDate, const OU FSNS(XML_w, XML_val), "dateTime"); m_pSerializer->singleElementNS(XML_w, XML_calendar, FSNS(XML_w, XML_val), "gregorian"); - m_pSerializer->endElementNS(XML_w, XML_date); + + if (aGrabBagSdt.hasElements()) + { + // There are some extra sdt parameters came from grab bag + SdtBlockHelper aSdtBlock; + aSdtBlock.GetSdtParamsFromGrabBag(aGrabBagSdt); + aSdtBlock.WriteExtraParams(m_pSerializer); + } + + m_pSerializer->endElementNS(XML_w, XML_sdtPr); + + m_pSerializer->startElementNS(XML_w, XML_sdtContent); +} + +void DocxAttributeOutput::WriteSdtPlainText(const OUString & sValue, const uno::Sequence<beans::PropertyValue>& aGrabBagSdt) +{ + m_pSerializer->startElementNS(XML_w, XML_sdt); + m_pSerializer->startElementNS(XML_w, XML_sdtPr); + + if (aGrabBagSdt.hasElements()) + { + // There are some extra sdt parameters came from grab bag + SdtBlockHelper aSdtBlock; + aSdtBlock.GetSdtParamsFromGrabBag(aGrabBagSdt); + aSdtBlock.WriteExtraParams(m_pSerializer); + + if (aSdtBlock.m_nSdtPrToken && aSdtBlock.m_nSdtPrToken != FSNS(XML_w, XML_id)) + { + // Write <w:text/> or whatsoever from grabbag + m_pSerializer->singleElement(aSdtBlock.m_nSdtPrToken); + } + + // Store databindings data for later writing to corresponding XMLs + OUString sPrefixMapping, sXpath; + for (const auto& rProp : std::as_const(aGrabBagSdt)) + { + if (rProp.Name == "ooxml:CT_SdtPr_dataBinding") + { + uno::Sequence<beans::PropertyValue> aDataBindingProps; + rProp.Value >>= aDataBindingProps; + for (const auto& rDBProp : std::as_const(aDataBindingProps)) + { + if (rDBProp.Name == "ooxml:CT_DataBinding_prefixMappings") + sPrefixMapping = rDBProp.Value.get<OUString>(); + else if (rDBProp.Name == "ooxml:CT_DataBinding_xpath") + sXpath = rDBProp.Value.get<OUString>(); + } + } + } + + if (sXpath.getLength()) + { + // Given xpath is sufficient + m_rExport.AddSdtData(sPrefixMapping, sXpath, sValue); + } + } + m_pSerializer->endElementNS(XML_w, XML_sdtPr); m_pSerializer->startElementNS(XML_w, XML_sdtContent); @@ -1996,6 +2239,7 @@ void DocxAttributeOutput::StartField_Impl( const SwTextNode* pNode, sal_Int32 nP { // Expand unsupported fields RunText( rInfos.pField->GetFieldName() ); + return; } else if ( rInfos.eType == ww::eFORMDATE ) { @@ -2023,7 +2267,11 @@ void DocxAttributeOutput::StartField_Impl( const SwTextNode* pNode, sal_Int32 nP OUString sLang; params.extractParam( ODF_FORMDATE_DATEFORMAT_LANGUAGE, sLang ); - WriteFormDateStart( sFullDate, sDateFormat, sLang ); + uno::Sequence<beans::PropertyValue> aSdtParams; + params.extractParam(UNO_NAME_MISC_OBJ_INTEROPGRABBAG, aSdtParams); + + WriteFormDateStart( sFullDate, sDateFormat, sLang, aSdtParams); + return; } else if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField) { @@ -2032,8 +2280,20 @@ void DocxAttributeOutput::StartField_Impl( const SwTextNode* pNode, sal_Int32 nP WriteSdtDropDownStart(rField2.GetName(), rField2.GetSelectedItem(), rField2.GetItemSequence()); + return; + } + else if (rInfos.eType == ww::eFILLIN) + { + SwInputField const& rField(*static_cast<SwInputField const*>(rInfos.pField.get())); + if (rField.getGrabBagParams().hasElements()) + { + WriteSdtPlainText(rField.GetPar1(), rField.getGrabBagParams()); + m_sRawText = rField.GetPar1(); // Write field content also as a fallback + return; + } } - else if ( rInfos.eType != ww::eNONE ) // HYPERLINK fields are just commands + + if ( rInfos.eType != ww::eNONE ) // HYPERLINK fields are just commands { if ( bWriteRun ) m_pSerializer->startElementNS(XML_w, XML_r); @@ -2239,7 +2499,7 @@ void DocxAttributeOutput::EndField_Impl( const SwTextNode* pNode, sal_Int32 nPos WriteSdtEnd(); return; } - if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField) + else if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField) { // write selected item from End not Start to ensure that any bookmarks // precede it @@ -2247,7 +2507,15 @@ void DocxAttributeOutput::EndField_Impl( const SwTextNode* pNode, sal_Int32 nPos WriteSdtDropDownEnd(rField.GetSelectedItem(), rField.GetItemSequence()); return; } - + else if (rInfos.eType == ww::eFILLIN && rInfos.pField) + { + SwInputField const& rField(*static_cast<SwInputField const*>(rInfos.pField.get())); + if (rField.getGrabBagParams().hasElements()) + { + WriteSdtEnd(); + return; + } + } // The command has to be written before for the hyperlinks if ( rInfos.pField ) { @@ -2688,7 +2956,7 @@ void DocxAttributeOutput::GetSdtEndBefore(const SdrObject* pSdrObj) auto pProp = std::find_if(aGrabBag.begin(), aGrabBag.end(), [this](const beans::PropertyValue& rProp) { - return "SdtEndBefore" == rProp.Name && m_bStartedCharSdt && !m_bEndCharSdt; }); + return "SdtEndBefore" == rProp.Name && m_aRunSdt.m_bStartedSdt && !m_bEndCharSdt; }); if (pProp != aGrabBag.end()) pProp->Value >>= m_bEndCharSdt; } @@ -5787,8 +6055,10 @@ void DocxAttributeOutput::pushToTableExportContext(DocxTableExportContext& rCont rContext.m_nTableDepth = m_tableReference->m_nTableDepth; m_tableReference->m_nTableDepth = 0; - rContext.m_bStartedParaSdt = m_bStartedParaSdt; - m_bStartedParaSdt = false; + rContext.m_bStartedParaSdt = m_aParagraphSdt.m_bStartedSdt; + m_aParagraphSdt.m_bStartedSdt = false; + rContext.m_bStartedRunSdt = m_aRunSdt.m_bStartedSdt; + m_aRunSdt.m_bStartedSdt = false; } void DocxAttributeOutput::popFromTableExportContext(DocxTableExportContext const & rContext) @@ -5796,7 +6066,8 @@ void DocxAttributeOutput::popFromTableExportContext(DocxTableExportContext const m_rExport.m_pTableInfo = rContext.m_pTableInfo; m_tableReference->m_bTableCellOpen = rContext.m_bTableCellOpen; m_tableReference->m_nTableDepth = rContext.m_nTableDepth; - m_bStartedParaSdt = rContext.m_bStartedParaSdt; + m_aParagraphSdt.m_bStartedSdt = rContext.m_bStartedParaSdt; + m_aRunSdt.m_bStartedSdt = rContext.m_bStartedRunSdt; } void DocxAttributeOutput::WriteTextBox(uno::Reference<drawing::XShape> xShape) @@ -6127,11 +6398,10 @@ void DocxAttributeOutput::SectionBreak( sal_uInt8 nC, bool bBreakAfter, const WW void DocxAttributeOutput::EndParaSdtBlock() { - if (m_bStartedParaSdt) + if (m_aParagraphSdt.m_bStartedSdt) { // Paragraph-level SDT still open? Close it now. - EndSdtBlock(); - m_bStartedParaSdt = false; + m_aParagraphSdt.EndSdtBlock(m_pSerializer); } } @@ -8966,102 +9236,8 @@ void DocxAttributeOutput::ParaGrabBag(const SfxGrabBagItem& rItem) { const uno::Sequence<beans::PropertyValue> aGrabBagSdt = rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >(); - for (const beans::PropertyValue& aPropertyValue : aGrabBagSdt) - { - if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj" || - aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList") - { - if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj") - m_nParagraphSdtPrToken = FSNS( XML_w, XML_docPartObj ); - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList") - m_nParagraphSdtPrToken = FSNS( XML_w, XML_docPartList ); - - uno::Sequence<beans::PropertyValue> aGrabBag; - aPropertyValue.Value >>= aGrabBag; - for (const auto& rProp : std::as_const(aGrabBag)) - { - OUString sValue = rProp.Value.get<OUString>(); - if (rProp.Name == "ooxml:CT_SdtDocPart_docPartGallery") - AddToAttrList( m_pParagraphSdtPrTokenChildren, - FSNS( XML_w, XML_docPartGallery ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartCategory") - AddToAttrList( m_pParagraphSdtPrTokenChildren, - FSNS( XML_w, XML_docPartCategory ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartUnique") - { - if (sValue.isEmpty()) - sValue = "true"; - AddToAttrList( m_pParagraphSdtPrTokenChildren, FSNS( XML_w, XML_docPartUnique ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - } - } - } - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_equation") - m_nParagraphSdtPrToken = FSNS( XML_w, XML_equation ); - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_picture") - m_nParagraphSdtPrToken = FSNS( XML_w, XML_picture ); - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation") - m_nParagraphSdtPrToken = FSNS( XML_w, XML_citation ); - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_group") - m_nParagraphSdtPrToken = FSNS( XML_w, XML_group ); - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text") - m_nParagraphSdtPrToken = FSNS(XML_w, XML_text); - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" && !m_pParagraphSdtPrDataBindingAttrs.is()) - { - uno::Sequence<beans::PropertyValue> aGrabBag; - aPropertyValue.Value >>= aGrabBag; - for (const auto& rProp : std::as_const(aGrabBag)) - { - OUString sValue = rProp.Value.get<OUString>(); - if (rProp.Name == "ooxml:CT_DataBinding_prefixMappings") - AddToAttrList( m_pParagraphSdtPrDataBindingAttrs, - FSNS( XML_w, XML_prefixMappings ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - else if (rProp.Name == "ooxml:CT_DataBinding_xpath") - AddToAttrList( m_pParagraphSdtPrDataBindingAttrs, - FSNS( XML_w, XML_xpath ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - else if (rProp.Name == "ooxml:CT_DataBinding_storeItemID") - AddToAttrList( m_pParagraphSdtPrDataBindingAttrs, - FSNS( XML_w, XML_storeItemID ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - } - } - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && m_aParagraphSdtPrAlias.isEmpty()) - { - if (!(aPropertyValue.Value >>= m_aParagraphSdtPrAlias)) - SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unexpected sdt alias value"); - m_aStartedParagraphSdtPrAlias = m_aParagraphSdtPrAlias; - } - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox") - { - m_nParagraphSdtPrToken = FSNS( XML_w14, XML_checkbox ); - uno::Sequence<beans::PropertyValue> aGrabBag; - aPropertyValue.Value >>= aGrabBag; - for (const auto& rProp : std::as_const(aGrabBag)) - { - OUString sValue = rProp.Value.get<OUString>(); - if (rProp.Name == "ooxml:CT_SdtCheckbox_checked") - AddToAttrList( m_pParagraphSdtPrTokenChildren, - FSNS( XML_w14, XML_checked ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - else if (rProp.Name == "ooxml:CT_SdtCheckbox_checkedState") - AddToAttrList( m_pParagraphSdtPrTokenChildren, - FSNS( XML_w14, XML_checkedState ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - else if (rProp.Name == "ooxml:CT_SdtCheckbox_uncheckedState") - AddToAttrList( m_pParagraphSdtPrTokenChildren, - FSNS( XML_w14, XML_uncheckedState ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - } - } - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id") - m_bParagraphSdtHasId = true; - else - SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unhandled SdtPr grab bag property " << aPropertyValue.Name); - } + m_aParagraphSdt.GetSdtParamsFromGrabBag(aGrabBagSdt); + m_aStartedParagraphSdtPrAlias = m_aParagraphSdt.m_aAlias; } else if (rGrabBagElement.first == "ParaCnfStyle") { @@ -9192,72 +9368,14 @@ void DocxAttributeOutput::CharGrabBag( const SfxGrabBagItem& rItem ) } else if (rGrabBagElement.first == "SdtEndBefore") { - if (m_bStartedCharSdt) + if (m_aRunSdt.m_bStartedSdt) m_bEndCharSdt = true; } else if (rGrabBagElement.first == "SdtPr" && FLY_NOT_PROCESSED != m_nStateOfFlyFrame ) { const uno::Sequence<beans::PropertyValue> aGrabBagSdt = rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >(); - for (const beans::PropertyValue& aPropertyValue : aGrabBagSdt) - { - if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox") - { - m_nRunSdtPrToken = FSNS( XML_w14, XML_checkbox ); - uno::Sequence<beans::PropertyValue> aGrabBag; - aPropertyValue.Value >>= aGrabBag; - for (const auto& rProp : std::as_const(aGrabBag)) - { - OUString sValue = rProp.Value.get<OUString>(); - if (rProp.Name == "ooxml:CT_SdtCheckbox_checked") - AddToAttrList( m_pRunSdtPrTokenChildren, - FSNS( XML_w14, XML_checked ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - else if (rProp.Name == "ooxml:CT_SdtCheckbox_checkedState") - AddToAttrList( m_pRunSdtPrTokenChildren, - FSNS( XML_w14, XML_checkedState ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - else if (rProp.Name == "ooxml:CT_SdtCheckbox_uncheckedState") - AddToAttrList( m_pRunSdtPrTokenChildren, - FSNS( XML_w14, XML_uncheckedState ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - } - } - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" && !m_pRunSdtPrDataBindingAttrs.is()) - { - uno::Sequence<beans::PropertyValue> aGrabBag; - aPropertyValue.Value >>= aGrabBag; - for (const auto& rProp : std::as_const(aGrabBag)) - { - OUString sValue = rProp.Value.get<OUString>(); - if (rProp.Name == "ooxml:CT_DataBinding_prefixMappings") - AddToAttrList( m_pRunSdtPrDataBindingAttrs, - FSNS( XML_w, XML_prefixMappings ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - else if (rProp.Name == "ooxml:CT_DataBinding_xpath") - AddToAttrList( m_pRunSdtPrDataBindingAttrs, - FSNS( XML_w, XML_xpath ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - else if (rProp.Name == "ooxml:CT_DataBinding_storeItemID") - AddToAttrList( m_pRunSdtPrDataBindingAttrs, - FSNS( XML_w, XML_storeItemID ), - OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); - } - } - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && m_aRunSdtPrAlias.isEmpty()) - { - if (!(aPropertyValue.Value >>= m_aRunSdtPrAlias)) - SAL_WARN("sw.ww8", "DocxAttributeOutput::CharGrabBag: unexpected sdt alias value"); - } - //do not overwrite the parent node. - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text" && !m_pRunSdtPrTokenChildren.is()) - m_nRunSdtPrToken = FSNS( XML_w, XML_text ); - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id" && m_nRunSdtPrToken == 0) - // only write id token as a marker if no other exist - m_nRunSdtPrToken = FSNS( XML_w, XML_id ); - else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation") - m_nRunSdtPrToken = FSNS( XML_w, XML_citation ); - } + m_aRunSdt.GetSdtParamsFromGrabBag(aGrabBagSdt); } else SAL_INFO("sw.ww8", "DocxAttributeOutput::CharGrabBag: unhandled grab bag property " << rGrabBagElement.first); @@ -9270,8 +9388,6 @@ DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, const FSHelperPtr m_pSerializer( pSerializer ), m_rDrawingML( *pDrawingML ), m_bEndCharSdt(false), - m_bStartedCharSdt(false), - m_bStartedParaSdt(false), m_endPageRef( false ), m_pFootnotesList( new ::docx::FootnotesList() ), m_pEndnotesList( new ::docx::FootnotesList() ), @@ -9317,10 +9433,7 @@ DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, const FSHelperPtr m_nParaBeforeSpacing(0), m_nParaAfterSpacing(0), m_setFootnote(false) - , m_nParagraphSdtPrToken(0) - , m_nRunSdtPrToken(0) , m_nStateOfFlyFrame( FLY_NOT_PROCESSED ) - , m_bParagraphSdtHasId(false) { // Push initial items to the RelId cache. In case the document contains no // special streams (headers, footers, etc.) then these items are used @@ -9383,26 +9496,4 @@ void DocxAttributeOutput::BulletDefinition(int nId, const Graphic& rGraphic, Siz m_pSerializer->endElementNS(XML_w, XML_numPicBullet); } -void DocxAttributeOutput::AddToAttrList( rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrName, const sal_Char* sAttrValue ) -{ - AddToAttrList( pAttrList, 1, nAttrName, sAttrValue ); -} - -void DocxAttributeOutput::AddToAttrList( rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrs, ... ) -{ - if( !pAttrList.is() ) - pAttrList = FastSerializerHelper::createAttrList(); - - va_list args; - va_start( args, nAttrs ); - for( sal_Int32 i = 0; i<nAttrs; i++) - { - sal_Int32 nName = va_arg( args, sal_Int32 ); - const char* pValue = va_arg( args, const char* ); - if( pValue ) - pAttrList->add( nName, pValue ); - } - va_end( args ); -} - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx index a7733d62209b..08b2aa9c8cf5 100644 --- a/sw/source/filter/ww8/docxattributeoutput.hxx +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -119,6 +119,37 @@ struct TableReference } }; +class SdtBlockHelper +{ +public: + SdtBlockHelper() + : m_bHasId(false) + , m_bStartedSdt(false) + , m_nSdtPrToken(0) + {} + + bool m_bHasId; + bool m_bStartedSdt; + rtl::Reference<sax_fastparser::FastAttributeList> m_pTokenChildren; + rtl::Reference<sax_fastparser::FastAttributeList> m_pTokenAttributes; + rtl::Reference<sax_fastparser::FastAttributeList> m_pTextAttrs; + rtl::Reference<sax_fastparser::FastAttributeList> m_pDataBindingAttrs; + OUString m_aColor; + OUString m_aPlaceHolderDocPart; + OUString m_aAlias; + sal_Int32 m_nSdtPrToken; + + void DeleteAndResetTheLists(); + + void WriteSdtBlock(::sax_fastparser::FSHelperPtr& pSerializer, bool bRunTextIsOn, bool bParagraphHasDrawing); + void WriteExtraParams(::sax_fastparser::FSHelperPtr& pSerializer); + + /// Closes a currently open SDT block. + void EndSdtBlock(::sax_fastparser::FSHelperPtr& pSerializer); + + void GetSdtParamsFromGrabBag(const uno::Sequence<beans::PropertyValue>& aGrabBagSdt); +}; + /// The class that has handlers for various resource types when exporting as DOCX. class DocxAttributeOutput : public AttributeOutputBase, public oox::vml::VMLTextExport, public oox::drawingml::DMLTextExport { @@ -708,16 +739,8 @@ private: void WritePostponedDMLDrawing(); void WritePostponedCustomShape(); - void WriteSdtBlock(sal_Int32& nSdtPrToken, - rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenChildren, - rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenAttributes, - rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrDataBindingAttrs, - OUString& rSdtPrAlias, - bool bPara); - /// Closes a currently open SDT block. - void EndSdtBlock(); - - void WriteFormDateStart(const OUString& sFullDate, const OUString& sDateFormat, const OUString& sLang); + void WriteFormDateStart(const OUString& sFullDate, const OUString& sDateFormat, const OUString& sLang, const uno::Sequence<beans::PropertyValue>& aGrabBagSdt); + void WriteSdtPlainText(const OUString& sValue, const uno::Sequence<beans::PropertyValue>& aGrabBagSdt); void WriteSdtDropDownStart(OUString const& rName, OUString const& rSelected, uno::Sequence<OUString> const& rListItems); void WriteSdtDropDownEnd(OUString const& rSelected, uno::Sequence<OUString> const& rListItems); void WriteSdtEnd(); @@ -729,9 +752,6 @@ private: void EndField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos& rInfos ); void DoWriteFieldRunProperties( const SwTextNode* pNode, sal_Int32 nPos, bool bWriteCombChars = false ); - static void AddToAttrList( rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrName, const sal_Char* sAttrValue ); - static void AddToAttrList( rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nArgs, ... ); - rtl::Reference<sax_fastparser::FastAttributeList> m_pFontsAttrList; rtl::Reference<sax_fastparser::FastAttributeList> m_pEastAsianLayoutAttrList; rtl::Reference<sax_fastparser::FastAttributeList> m_pCharLangAttrList; @@ -740,10 +760,6 @@ private: rtl::Reference<sax_fastparser::FastAttributeList> m_pHyperlinkAttrList; /// If the current SDT around runs should be ended before the current run. bool m_bEndCharSdt; - /// If an SDT around runs is currently open. - bool m_bStartedCharSdt; - /// If an SDT around paragraphs is currently open. - bool m_bStartedParaSdt; /// Attributes of the run color rtl::Reference<sax_fastparser::FastAttributeList> m_pColorAttrList; /// Attributes of the paragraph background @@ -954,24 +970,14 @@ private: /// RelId <-> BitmapChecksum cache, similar to m_aRelIdCache, but used for non-Writer graphics, handled in oox. std::stack< std::map<BitmapChecksum, OUString> > m_aSdrRelIdCache; - /// members to control the existence of grabbagged SDT properties in the paragraph - sal_Int32 m_nParagraphSdtPrToken; - rtl::Reference<sax_fastparser::FastAttributeList> m_pParagraphSdtPrTokenChildren; - rtl::Reference<sax_fastparser::FastAttributeList> m_pParagraphSdtPrTokenAttributes; - rtl::Reference<sax_fastparser::FastAttributeList> m_pParagraphSdtPrDataBindingAttrs; - /// members to control the existence of grabbagged SDT properties in the text run - sal_Int32 m_nRunSdtPrToken; + SdtBlockHelper m_aParagraphSdt; + SdtBlockHelper m_aRunSdt; + /// State of the Fly at current position FlyProcessingState m_nStateOfFlyFrame; - rtl::Reference<sax_fastparser::FastAttributeList> m_pRunSdtPrTokenChildren; - rtl::Reference<sax_fastparser::FastAttributeList> m_pRunSdtPrDataBindingAttrs; - /// Value of the <w:alias> paragraph SDT element. - OUString m_aParagraphSdtPrAlias; + /// Same as m_aParagraphSdtPrAlias, but its content is available till the SDT is closed. OUString m_aStartedParagraphSdtPrAlias; - OUString m_aRunSdtPrAlias; - /// Currently paragraph SDT has a <w:id> child element. - bool m_bParagraphSdtHasId; std::map<SvxBoxItemLine, css::table::BorderLine2> m_aTableStyleConf; @@ -1039,6 +1045,7 @@ struct DocxTableExportContext ww8::WW8TableInfo::Pointer_t m_pTableInfo; bool m_bTableCellOpen; bool m_bStartedParaSdt; + bool m_bStartedRunSdt; sal_uInt32 m_nTableDepth; DocxTableExportContext(DocxAttributeOutput& rOutput) : m_rOutput(rOutput) { m_rOutput.pushToTableExportContext(*this); } ~DocxTableExportContext() { m_rOutput.popFromTableExportContext(*this); } diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx index a48f4fb7b59e..20a021ab1dfe 100644 --- a/sw/source/filter/ww8/docxexport.cxx +++ b/sw/source/filter/ww8/docxexport.cxx @@ -34,6 +34,12 @@ #include <com/sun/star/xml/sax/Writer.hpp> #include <com/sun/star/awt/XControlModel.hpp> #include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/io/XStreamListener.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/text/XTextFieldsSupplier.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/xml/xslt/XSLTTransformer.hpp> #include <oox/token/namespaces.hxx> #include <oox/token/tokens.hxx> @@ -49,6 +55,8 @@ #include <map> #include <algorithm> +#include <condition_variable> +#include <mutex> #include <IMark.hxx> #include <IDocumentSettingAccess.hxx> @@ -1340,6 +1348,77 @@ void DocxExport::WriteGlossary() } } +namespace { + class XsltTransformListener : public ::cppu::WeakImplHelper<io::XStreamListener> + { + public: + XsltTransformListener() : m_bDone(false) {} + + void wait() { + std::unique_lock<std::mutex> g(m_mutex); + m_cond.wait(g, [this]() { return m_bDone; }); + } + + private: + std::mutex m_mutex; + std::condition_variable m_cond; + bool m_bDone; + + virtual void SAL_CALL disposing(const lang::EventObject&) noexcept override {} + virtual void SAL_CALL started() noexcept override {} + virtual void SAL_CALL closed() noexcept override { notifyDone(); } + virtual void SAL_CALL terminated() noexcept override { notifyDone(); } + virtual void SAL_CALL error(const uno::Any& e) override + { + notifyDone(); // set on error too, otherwise main thread waits forever + SAL_WARN("sw.ww8", e); + } + + void notifyDone() { + std::scoped_lock<std::mutex> g(m_mutex); + m_bDone = true; + m_cond.notify_all(); + } + }; +} + +static void lcl_UpdateXmlValues(const SdtData& sdtData, const uno::Reference<css::io::XInputStream>& xInputStream, const uno::Reference<css::io::XOutputStream>& xOutputStream) +{ + uno::Sequence<uno::Any> aArgs{ + // XSLT transformation stylesheet: + // - write all elements as is + // - but if element mathes sdtData.xpath, replace it's text content by sdtData.xpath + uno::Any(beans::NamedValue("StylesheetText", uno::Any(OUString("<?xml version=\"1.0\" encoding=\"UTF-8\"?> \ +<xsl:stylesheet\ + xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\ + " + sdtData.namespaces + "\ + version=\"1.0\">\ + <xsl:template match=\"@* | node()\">\ + <xsl:copy>\ + <xsl:apply-templates select=\"@* | node()\"/>\ + </xsl:copy>\ + </xsl:template>\ + <xsl:template match = \"" + sdtData.xpath + "\">\ + <xsl:copy>\ + <xsl:text>" + sdtData.data + "</xsl:text>\ + </xsl:copy>\ + </xsl:template>\ +</xsl:stylesheet>\ +")))) + }; + + css::uno::Reference<css::xml::xslt::XXSLTTransformer> xTransformer = + css::xml::xslt::XSLTTransformer::create(comphelper::getProcessComponentContext(), aArgs); + xTransformer->setInputStream(xInputStream); + xTransformer->setOutputStream(xOutputStream); + + rtl::Reference<XsltTransformListener> xListener = new XsltTransformListener(); + xTransformer->addListener(xListener.get()); + + xTransformer->start(); + xListener->wait(); +} + void DocxExport::WriteCustomXml() { uno::Reference< beans::XPropertySet > xPropSet( m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW ); @@ -1375,10 +1454,54 @@ void DocxExport::WriteCustomXml() uno::Reference< xml::sax::XSAXSerializable > serializer( customXmlDom, uno::UNO_QUERY ); uno::Reference< xml::sax::XWriter > writer = xml::sax::Writer::create( comphelper::getProcessComponentContext() ); - writer->setOutputStream( GetFilter().openFragmentStream( "customXml/item"+OUString::number((j+1))+".xml", - "application/xml" ) ); - serializer->serialize( uno::Reference< xml::sax::XDocumentHandler >( writer, uno::UNO_QUERY_THROW ), - uno::Sequence< beans::StringPair >() ); + + uno::Reference < css::io::XOutputStream > xOutStream = GetFilter().openFragmentStream("customXml/item" + OUString::number(j + 1) + ".xml", + "application/xml"); + if (m_SdtData.size()) + { + // There are some SDT blocks data with data bindings which can update some custom xml values + uno::Reference< io::XStream > xMemStream( + comphelper::getProcessComponentContext()->getServiceManager()->createInstanceWithContext("com.sun.star.comp.MemoryStream", + comphelper::getProcessComponentContext()), + uno::UNO_QUERY_THROW); + + writer->setOutputStream(xMemStream->getOutputStream()); + + serializer->serialize(uno::Reference< xml::sax::XDocumentHandler >(writer, uno::UNO_QUERY_THROW), + uno::Sequence< beans::StringPair >()); + + uno::Reference< io::XStream > xXSLTInStream = xMemStream; + uno::Reference< io::XStream > xXSLTOutStream; + // Apply XSLT transformations for each SDT data binding + // Seems it is not possible to do this as one transformation: each data binding + // can have different namespaces, but with conflicting names (ns0, ns1, etc..) + for (size_t i = 0; i < m_SdtData.size(); i++) + { + if (i == m_SdtData.size() - 1) + { + // last transformation + lcl_UpdateXmlValues(m_SdtData[i], xXSLTInStream->getInputStream(), xOutStream); + } + else + { + xXSLTOutStream.set( + comphelper::getProcessComponentContext()->getServiceManager()->createInstanceWithContext("com.sun.star.comp.MemoryStream", + comphelper::getProcessComponentContext()), + uno::UNO_QUERY_THROW); + lcl_UpdateXmlValues(m_SdtData[i], xXSLTInStream->getInputStream(), xXSLTOutStream->getOutputStream()); + // Use previous output as an input for next run + xXSLTInStream.set( xXSLTOutStream ); + } + } + + } + else + { + writer->setOutputStream(xOutStream); + + serializer->serialize(uno::Reference< xml::sax::XDocumentHandler >(writer, uno::UNO_QUERY_THROW), + uno::Sequence< beans::StringPair >()); + } } if (customXmlDomProps.is()) @@ -1591,7 +1714,8 @@ XFastAttributeListRef DocxExport::MainXmlNamespaces() pAttr->add( FSNS( XML_xmlns, XML_mc ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(mce)), RTL_TEXTENCODING_UTF8).getStr() ); pAttr->add( FSNS( XML_xmlns, XML_wp14 ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(wp14)), RTL_TEXTENCODING_UTF8).getStr() ); pAttr->add( FSNS( XML_xmlns, XML_w14 ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(w14)), RTL_TEXTENCODING_UTF8).getStr() ); - pAttr->add( FSNS( XML_mc, XML_Ignorable ), "w14 wp14" ); + pAttr->add( FSNS( XML_xmlns, XML_w15 ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(w15)), RTL_TEXTENCODING_UTF8).getStr() ); + pAttr->add( FSNS( XML_mc, XML_Ignorable ), "w14 wp14 w15" ); return XFastAttributeListRef( pAttr ); } diff --git a/sw/source/filter/ww8/docxexport.hxx b/sw/source/filter/ww8/docxexport.hxx index 00b908dc7efa..dc2dba5b14cb 100644 --- a/sw/source/filter/ww8/docxexport.hxx +++ b/sw/source/filter/ww8/docxexport.hxx @@ -61,6 +61,14 @@ struct DocxSettingsData bool trackRevisions; // Should 'Track Revisions' be set }; +/// Data to keep and write to XMLs +struct SdtData +{ + OUString namespaces; + OUString xpath; + OUString data; +}; + /// The class that does all the actual DOCX export-related work. class DocxExport : public MSWordExportBase { @@ -114,6 +122,9 @@ class DocxExport : public MSWordExportBase /// Pointer to the Frame of a floating table it is nested in const ww8::Frame *m_pFloatingTableFrame = nullptr; + /// Storage for sdt data which need to be written to other XMLs + std::vector<SdtData> m_SdtData; + public: DocxExportFilter& GetFilter() { return *m_pFilter; }; @@ -195,6 +206,11 @@ public: virtual ExportFormat GetExportFormat() const override { return ExportFormat::DOCX; } + void AddSdtData(const OUString & namespaces, const OUString & xpath, const OUString & data) + { + m_SdtData.push_back({ namespaces, xpath, data }); + } + protected: /// Format-dependent part of the actual export. virtual ErrCode ExportDocument_Impl() override; diff --git a/sw/source/filter/ww8/docxsdrexport.cxx b/sw/source/filter/ww8/docxsdrexport.cxx index 7cacc4eb3173..dd0455674649 100644 --- a/sw/source/filter/ww8/docxsdrexport.cxx +++ b/sw/source/filter/ww8/docxsdrexport.cxx @@ -17,6 +17,7 @@ #include <editeng/boxitem.hxx> #include <svx/svdogrp.hxx> #include <oox/token/namespaces.hxx> +#include <oox/token/relationship.hxx> #include <textboxhelper.hxx> #include <fmtanchr.hxx> #include <fmtsrnd.hxx> @@ -873,8 +874,21 @@ void DocxSdrExport::writeDMLDrawing(const SdrObject* pSdrObject, const SwFrameFo && pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) pDocPrAttrList->add(XML_hidden, OString::number(1).getStr()); - sax_fastparser::XFastAttributeListRef xDocPrAttrListRef(pDocPrAttrList); - pFS->singleElementNS(XML_wp, XML_docPr, xDocPrAttrListRef); + + pFS->startElementNS(XML_wp, XML_docPr, pDocPrAttrList); + OUString sHyperlink = pSdrObject->getHyperlink(); + if (!sHyperlink.isEmpty()) + { + OUString sRelId = m_pImpl->getExport().GetFilter().addRelation( + pFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK), + oox::drawingml::URLTransformer().getTransformedString(sHyperlink), + oox::drawingml::URLTransformer().isExternalURL(sHyperlink)); + pFS->singleElementNS( + XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId.toUtf8().getStr(), + FSNS(XML_xmlns, XML_a), + m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)).toUtf8()); + } + pFS->endElementNS(XML_wp, XML_docPr); uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW); const char* pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape"; @@ -1212,7 +1226,24 @@ void DocxSdrExport::writeDMLTextFrame(ww8::Frame const* pParentFrame, int nAncho pDocPrAttrList->add( XML_name, OUStringToOString(rFrameFormat.GetName(), RTL_TEXTENCODING_UTF8).getStr()); sax_fastparser::XFastAttributeListRef xDocPrAttrListRef(pDocPrAttrList); - pFS->singleElementNS(XML_wp, XML_docPr, xDocPrAttrListRef); + pFS->startElementNS(XML_wp, XML_docPr, pDocPrAttrList); + + OUString sHyperlink; + if (xPropertySet.is()) + xPropertySet->getPropertyValue("HyperLinkURL") >>= sHyperlink; + if (!sHyperlink.isEmpty()) + { + OUString sRelId = m_pImpl->getExport().GetFilter().addRelation( + pFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK), + oox::drawingml::URLTransformer().getTransformedString(sHyperlink), + oox::drawingml::URLTransformer().isExternalURL(sHyperlink)); + pFS->singleElementNS( + XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId.toUtf8().getStr(), + FSNS(XML_xmlns, XML_a), + m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)).toUtf8()); + } + + pFS->endElementNS(XML_wp, XML_docPr); pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a), m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)).toUtf8()); @@ -1493,6 +1524,16 @@ void DocxSdrExport::writeVMLTextFrame(ww8::Frame const* pParentFrame, bool bText if (!sAnchorId.isEmpty()) m_pImpl->getFlyAttrList()->addNS(XML_w14, XML_anchorId, OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8)); + + uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObject)->getUnoShape(), + uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); + OUString sHyperlink; + if (xShapeProps.is()) + xShapeProps->getPropertyValue("HyperLinkURL") >>= sHyperlink; + if (!sHyperlink.isEmpty()) + m_pImpl->getFlyAttrList()->add(XML_href, + OUStringToOString(sHyperlink, RTL_TEXTENCODING_UTF8)); } sax_fastparser::XFastAttributeListRef xFlyAttrList(m_pImpl->getFlyAttrList().get()); m_pImpl->getFlyAttrList().clear(); diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx b/sw/source/filter/ww8/rtfattributeoutput.cxx index 6313472bf728..cebf1ffcf430 100644 --- a/sw/source/filter/ww8/rtfattributeoutput.cxx +++ b/sw/source/filter/ww8/rtfattributeoutput.cxx @@ -109,7 +109,7 @@ static OString OutTBLBorderLine(RtfExport const& rExport, const editeng::SvxBord { case SvxBorderLineStyle::SOLID: { - if (DEF_LINE_WIDTH_0 == pLine->GetWidth()) + if (SvxBorderLineWidth::Hairline == pLine->GetWidth()) aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRHAIR); else aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRS); 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/wrtw8sty.cxx b/sw/source/filter/ww8/wrtw8sty.cxx index 8a315444a133..6228526a88ba 100644 --- a/sw/source/filter/ww8/wrtw8sty.cxx +++ b/sw/source/filter/ww8/wrtw8sty.cxx @@ -151,13 +151,13 @@ MSWordStyles::MSWordStyles( MSWordExportBase& rExport, bool bListStyles ) m_rExport.m_pDoc->GetFootnoteInfo().GetAnchorCharFormat( *m_rExport.m_pDoc ); m_rExport.m_pDoc->GetFootnoteInfo().GetCharFormat( *m_rExport.m_pDoc ); } - sal_uInt16 nAlloc = WW8_RESERVED_SLOTS + m_rExport.m_pDoc->GetCharFormats()->size() - 1 + + sal_uInt32 nAlloc = WW8_RESERVED_SLOTS + m_rExport.m_pDoc->GetCharFormats()->size() - 1 + m_rExport.m_pDoc->GetTextFormatColls()->size() - 1 + (bListStyles ? m_rExport.m_pDoc->GetNumRuleTable().size() - 1 : 0); + nAlloc = std::min<sal_uInt32>(nAlloc, MSWORD_MAX_STYLES_LIMIT); // somewhat generous ( free for up to 15 ) - m_pFormatA.reset( new SwFormat*[ nAlloc ] ); - memset( m_pFormatA.get(), 0, nAlloc * sizeof( SwFormat* ) ); + m_aFormatA.resize(nAlloc, nullptr); memset( m_aHeadingParagraphStyles, -1 , MAXLEVEL * sizeof( sal_uInt16)); BuildStylesTable(); @@ -173,7 +173,7 @@ sal_uInt16 MSWordStyles::GetSlot( const SwFormat* pFormat ) const { sal_uInt16 n; for ( n = 0; n < m_nUsedSlots; n++ ) - if ( m_pFormatA[n] == pFormat ) + if ( m_aFormatA[n] == pFormat ) return n; return 0xfff; // 0xfff: WW: zero } @@ -282,19 +282,19 @@ void MSWordStyles::BuildStylesTable() const SwCharFormats& rArr = *m_rExport.m_pDoc->GetCharFormats(); // first CharFormat // the default character style ( 0 ) will not be outputted ! - for( size_t n = 1; n < rArr.size(); n++ ) + for( size_t n = 1; n < rArr.size() && m_nUsedSlots < MSWORD_MAX_STYLES_LIMIT; n++ ) { SwCharFormat* pFormat = rArr[n]; - m_pFormatA[ BuildGetSlot( *pFormat ) ] = pFormat; + m_aFormatA[ BuildGetSlot( *pFormat ) ] = pFormat; } const SwTextFormatColls& rArr2 = *m_rExport.m_pDoc->GetTextFormatColls(); // then TextFormatColls // the default character style ( 0 ) will not be outputted ! - for( size_t n = 1; n < rArr2.size(); n++ ) + for( size_t n = 1; n < rArr2.size() && m_nUsedSlots < MSWORD_MAX_STYLES_LIMIT; n++ ) { SwTextFormatColl* pFormat = rArr2[n]; sal_uInt16 nId = BuildGetSlot( *pFormat ) ; - m_pFormatA[ nId ] = pFormat; + m_aFormatA[ nId ] = pFormat; if ( pFormat->IsAssignedToListLevelOfOutlineStyle() ) { int nLvl = pFormat->GetAssignedOutlineStyleLevel() ; @@ -307,7 +307,7 @@ void MSWordStyles::BuildStylesTable() return; const SwNumRuleTable& rNumRuleTable = m_rExport.m_pDoc->GetNumRuleTable(); - for (size_t i = 0; i < rNumRuleTable.size(); ++i) + for (size_t i = 0; i < rNumRuleTable.size() && m_nUsedSlots < MSWORD_MAX_STYLES_LIMIT; ++i) { const SwNumRule* pNumRule = rNumRuleTable[i]; if (pNumRule->IsAutoRule() || pNumRule->GetName().startsWith("WWNum")) @@ -347,8 +347,8 @@ void MSWordStyles::BuildStyleIds() for (sal_uInt16 n = 1; n < m_nUsedSlots; ++n) { OUString aName; - if(m_pFormatA[n]) - aName = m_pFormatA[n]->GetName(); + if (m_aFormatA[n]) + aName = m_aFormatA[n]->GetName(); else if (m_aNumRules.find(n) != m_aNumRules.end()) aName = m_aNumRules[n]->GetName(); @@ -612,8 +612,8 @@ void MSWordStyles::OutputStyle( SwFormat* pFormat, sal_uInt16 nPos ) for ( int nSuffix = 0; ; ++nSuffix ) { bool clash=false; for ( sal_uInt16 n = 1; n < m_nUsedSlots; ++n ) - if ( m_pFormatA[n] && - m_pFormatA[n]->GetName().equalsIgnoreAsciiCase(aName) ) + if ( m_aFormatA[n] && + m_aFormatA[n]->GetName().equalsIgnoreAsciiCase(aName) ) { clash = true; break; @@ -702,7 +702,7 @@ void MSWordStyles::OutputStylesTable() if (m_aNumRules.find(n) != m_aNumRules.end()) OutputStyle(m_aNumRules[n], n); else - OutputStyle( m_pFormatA[n], n ); + OutputStyle(m_aFormatA[n], n); } m_rExport.AttrOutput().EndStyles( m_nUsedSlots ); diff --git a/sw/source/filter/ww8/wrtww8.cxx b/sw/source/filter/ww8/wrtww8.cxx index 8d4add7b34bf..86899e1dba88 100644 --- a/sw/source/filter/ww8/wrtww8.cxx +++ b/sw/source/filter/ww8/wrtww8.cxx @@ -3673,7 +3673,6 @@ MSWordExportBase::MSWordExportBase( SwDoc *pDocument, std::shared_ptr<SwUnoCurso , m_nOrigRedlineFlags(RedlineFlags::NONE) , m_bOrigShowChanges(true) , m_pCurrentPageDesc(nullptr) - , m_bPrevTextNodeIsEmpty(false) , m_bFirstTOCNodeWithSection(false) , m_pChpIter(nullptr) , m_pAtn(nullptr) diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx index 9356b87bd86f..0e1cfea0a51a 100644 --- a/sw/source/filter/ww8/wrtww8.hxx +++ b/sw/source/filter/ww8/wrtww8.hxx @@ -496,7 +496,6 @@ public: std::vector<aBookmarkPair> m_aImplicitBookmarks; ww8::Frames m_aFrames; // The floating frames in this document const SwPageDesc *m_pCurrentPageDesc; - bool m_bPrevTextNodeIsEmpty; bool m_bFirstTOCNodeWithSection; std::unique_ptr<WW8_WrPlcPn> m_pPapPlc; std::unique_ptr<WW8_WrPlcPn> m_pChpPlc; @@ -657,6 +656,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 @@ -758,7 +758,7 @@ public: static sal_uLong GetSectionLineNo( const SfxItemSet* pSet, const SwNode& rNd ); /// Start new section. - void OutputSectionBreaks( const SfxItemSet *pSet, const SwNode& rNd, bool isCellOpen = false, bool isTextNodeEmpty = false); + void OutputSectionBreaks( const SfxItemSet *pSet, const SwNode& rNd, bool isCellOpen = false ); /// Write section properties. /// @@ -1576,7 +1576,7 @@ class MSWordStyles { MSWordExportBase& m_rExport; sal_uInt16 m_aHeadingParagraphStyles[MAXLEVEL]; - std::unique_ptr<SwFormat*[]> m_pFormatA; ///< Slot <-> Character and paragraph style array (0 for list styles). + std::vector<SwFormat*> m_aFormatA; ///< Slot <-> Character and paragraph style array (0 for list styles). sal_uInt16 m_nUsedSlots; bool const m_bListStyles; ///< If list styles are requested to be exported as well. std::map<sal_uInt16, const SwNumRule*> m_aNumRules; ///< Slot <-> List style map. @@ -1627,7 +1627,7 @@ public: /// Get styleId of the nId-th style (nId is its position in pFormatA). OString const & GetStyleId(sal_uInt16 nId) const; - const SwFormat* GetSwFormat(sal_uInt16 nId) const { return m_pFormatA[nId]; } + const SwFormat* GetSwFormat(sal_uInt16 nId) const { return m_aFormatA[nId]; } /// Get numbering rule of the nId-th style const SwNumRule* GetSwNumRule(sal_uInt16 nId) const; sal_uInt16 GetHeadingParagraphStyleId(sal_uInt16 nLevel) const { return m_aHeadingParagraphStyles[ nLevel ]; } diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx index 0b2c6527c0ea..cfd58d9d643d 100644 --- a/sw/source/filter/ww8/ww8atr.cxx +++ b/sw/source/filter/ww8/ww8atr.cxx @@ -441,7 +441,7 @@ bool MSWordExportBase::SetCurrentPageDescFromNode(const SwNode &rNd) * because that one only exits once for CHP and PAP and therefore end up in * the wrong one. */ -void MSWordExportBase::OutputSectionBreaks( const SfxItemSet *pSet, const SwNode& rNd, bool isCellOpen, bool isTextNodeEmpty) +void MSWordExportBase::OutputSectionBreaks( const SfxItemSet *pSet, const SwNode& rNd, bool isCellOpen ) { if ( m_bStyDef || m_bOutKF || m_bInWriteEscher || m_bOutPageDescs ) return; @@ -462,14 +462,10 @@ void MSWordExportBase::OutputSectionBreaks( const SfxItemSet *pSet, const SwNode // Even if pAktPageDesc != pPageDesc ,it might be because of the different header & footer types. if (m_pCurrentPageDesc != pPageDesc) { - if ( ( isCellOpen && ( m_pCurrentPageDesc->GetName() != pPageDesc->GetName() )) || - ( isTextNodeEmpty || m_bPrevTextNodeIsEmpty )) + if (isCellOpen && ( m_pCurrentPageDesc->GetName() != pPageDesc->GetName() )) { /* Do not output a section break in the following scenarios. 1) Table cell is open and page header types are different - 2) PageBreak is present but text node has no string - it is an empty node. - 3) If the previous node was an empty text node and current node is a non empty text node or vice versa. - 4) If previous node and current node both are empty text nodes. Converting a page break to section break would cause serious issues while importing the RT files with different first page being set. */ @@ -572,8 +568,6 @@ void MSWordExportBase::OutputSectionBreaks( const SfxItemSet *pSet, const SwNode { bNewPageDesc |= SetCurrentPageDescFromNode( rNd ); } - if( isTextNodeEmpty ) - bNewPageDesc = false; } if ( !bNewPageDesc ) AttrOutput().OutputItem( *pItem ); @@ -620,7 +614,6 @@ void MSWordExportBase::OutputSectionBreaks( const SfxItemSet *pSet, const SwNode PrepareNewPageDesc( pSet, rNd, pPgDesc, m_pCurrentPageDesc ); } m_bBreakBefore = false; - m_bPrevTextNodeIsEmpty = isTextNodeEmpty ; } // #i76300# @@ -3649,6 +3642,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 { @@ -4315,7 +4315,7 @@ WW8_BRCVer9 WW8Export::TranslateBorderLine(const SvxBorderLine& rLine, { case SvxBorderLineStyle::SOLID: { - if ( rLine.GetWidth( ) == DEF_LINE_WIDTH_0 ) + if ( rLine.GetWidth( ) == SvxBorderLineWidth::Hairline ) brcType = 5; else brcType = 1; diff --git a/sw/source/filter/ww8/ww8par.cxx b/sw/source/filter/ww8/ww8par.cxx index f5bdacffe32d..41beb52670ff 100644 --- a/sw/source/filter/ww8/ww8par.cxx +++ b/sw/source/filter/ww8/ww8par.cxx @@ -1992,7 +1992,7 @@ void SwWW8ImplReader::ImportDopTypography(const WW8DopTypography &rTypo) * Footnotes and Endnotes */ WW8ReaderSave::WW8ReaderSave(SwWW8ImplReader* pRdr ,WW8_CP nStartCp) : - maTmpPos(*pRdr->m_pPaM->GetPoint()), + mxTmpPos(pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_pPaM->GetPoint())), mxOldStck(std::move(pRdr->m_xCtrlStck)), mxOldAnchorStck(std::move(pRdr->m_xAnchorStck)), mxOldRedlines(std::move(pRdr->m_xRedlineStack)), @@ -2076,12 +2076,21 @@ void WW8ReaderSave::Restore( SwWW8ImplReader* pRdr ) pRdr->m_xRedlineStack->closeall(*pRdr->m_pPaM->GetPoint()); pRdr->m_aFrameRedlines.emplace(std::move(pRdr->m_xRedlineStack)); + + // ofz#37322 drop m_pLastAnchorPos during RedlineStack dtor and restore it afterwards to the same + // place, or somewhere close if that place got destroyed + std::shared_ptr<SwUnoCursor> xLastAnchorCursor(pRdr->m_pLastAnchorPos ? pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_pLastAnchorPos) : nullptr); + pRdr->m_pLastAnchorPos.reset(); + pRdr->m_xRedlineStack = std::move(mxOldRedlines); + if (xLastAnchorCursor) + pRdr->m_pLastAnchorPos.reset(new SwPosition(*xLastAnchorCursor->GetPoint())); + pRdr->DeleteAnchorStack(); pRdr->m_xAnchorStck = std::move(mxOldAnchorStck); - *pRdr->m_pPaM->GetPoint() = maTmpPos; + *pRdr->m_pPaM->GetPoint() = GetStartPos(); if (mxOldPlcxMan != pRdr->m_xPlcxMan) pRdr->m_xPlcxMan = mxOldPlcxMan; diff --git a/sw/source/filter/ww8/ww8par.hxx b/sw/source/filter/ww8/ww8par.hxx index 74953aa2016c..f98853d82295 100644 --- a/sw/source/filter/ww8/ww8par.hxx +++ b/sw/source/filter/ww8/ww8par.hxx @@ -589,7 +589,7 @@ class WW8ReaderSave { private: WW8PLCFxSaveAll maPLCFxSave; - SwPosition const maTmpPos; + std::shared_ptr<SwUnoCursor> mxTmpPos; std::deque<bool> maOldApos; std::deque<WW8FieldEntry> maOldFieldStack; std::unique_ptr<SwWW8FltControlStack> mxOldStck; @@ -617,7 +617,7 @@ private: public: WW8ReaderSave(SwWW8ImplReader* pRdr, WW8_CP nStart=-1); void Restore(SwWW8ImplReader* pRdr); - const SwPosition &GetStartPos() const { return maTmpPos; } + const SwPosition &GetStartPos() const { return *mxTmpPos->GetPoint(); } }; enum class eF_ResT { OK, TEXT, TAGIGN, READ_FSPA }; @@ -922,6 +922,14 @@ public: explicit wwExtraneousParas(SwDoc &rDoc) : m_rDoc(rDoc) {} ~wwExtraneousParas() { delete_all_from_doc(); } void insert(SwTextNode *pTextNode) { m_aTextNodes.insert(pTextNode); } + void check_anchor_destination(SwTextNode *pTextNode) + { + auto it = m_aTextNodes.find(pTextNode); + if (it == m_aTextNodes.end()) + return; + SAL_WARN("sw.ww8", "It is unexpected to anchor something in a para scheduled for removal"); + m_aTextNodes.erase(it); + } void delete_all_from_doc(); }; diff --git a/sw/source/filter/ww8/ww8par2.cxx b/sw/source/filter/ww8/ww8par2.cxx index 6edc842e48ee..026b0ead213c 100644 --- a/sw/source/filter/ww8/ww8par2.cxx +++ b/sw/source/filter/ww8/ww8par2.cxx @@ -2749,8 +2749,17 @@ void WW8TabDesc::FinishSwTable() { m_pIo->m_xRedlineStack->closeall(*m_pIo->m_pPaM->GetPoint()); m_pIo->m_aFrameRedlines.emplace(std::move(m_pIo->m_xRedlineStack)); + + // ofz#38011 drop m_pLastAnchorPos during RedlineStack dtor and restore it afterwards to the same + // place, or somewhere close if that place got destroyed + std::shared_ptr<SwUnoCursor> xLastAnchorCursor(m_pIo->m_pLastAnchorPos ? m_pIo->m_rDoc.CreateUnoCursor(*m_pIo->m_pLastAnchorPos) : nullptr); + m_pIo->m_pLastAnchorPos.reset(); + m_pIo->m_xRedlineStack = std::move(mxOldRedlineStack); + if (xLastAnchorCursor) + m_pIo->m_pLastAnchorPos.reset(new SwPosition(*xLastAnchorCursor->GetPoint())); + WW8DupProperties aDup(m_pIo->m_rDoc,m_pIo->m_xCtrlStck.get()); m_pIo->m_xCtrlStck->SetAttr( *m_pIo->m_pPaM->GetPoint(), 0, false); diff --git a/sw/source/filter/ww8/ww8par6.cxx b/sw/source/filter/ww8/ww8par6.cxx index 1e68d7a592b4..ca2ddc28fd1f 100644 --- a/sw/source/filter/ww8/ww8par6.cxx +++ b/sw/source/filter/ww8/ww8par6.cxx @@ -2451,6 +2451,9 @@ bool SwWW8ImplReader::StartApo(const ApoTestResults &rApo, const WW8_TablePos *p } else { + // ofz#34749 we shouldn't anchor anything into an 'extra' paragraph scheduled for + // removal at end of import, but check if that scenario is happening + m_aExtraneousParas.check_anchor_destination(m_pPaM->GetNode().GetTextNode()); m_xSFlyPara->pFlyFormat = m_rDoc.MakeFlySection(WW8SwFlyPara::eAnchor, m_pPaM->GetPoint(), &aFlySet); OSL_ENSURE(m_xSFlyPara->pFlyFormat->GetAnchor().GetAnchorId() == diff --git a/sw/source/filter/xml/XMLRedlineImportHelper.cxx b/sw/source/filter/xml/XMLRedlineImportHelper.cxx index bfd4e488fcb8..0a0c75bc9816 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,17 @@ 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.SetMark(); + *aPaM.GetMark() = SwPosition(*pRedlineInfo->pContentIndex->GetNode().EndOfSectionNode()); + pDoc->getIDocumentContentOperations().DeleteRange(aPaM); + } 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/xmlithlp.cxx b/sw/source/filter/xml/xmlithlp.cxx index 6dc8f3830e77..f3d8df67e1e6 100644 --- a/sw/source/filter/xml/xmlithlp.cxx +++ b/sw/source/filter/xml/xmlithlp.cxx @@ -73,11 +73,10 @@ const struct SvXMLEnumMapEntry<sal_uInt16> psXML_NamedBorderWidths[] = }; // mapping tables to map external xml input to internal box line widths -const sal_uInt16 aBorderWidths[] = -{ - DEF_LINE_WIDTH_0, - DEF_LINE_WIDTH_5, - DEF_LINE_WIDTH_1, +const sal_uInt16 aBorderWidths[] = { + SvxBorderLineWidth::Hairline, + SvxBorderLineWidth::VeryThin, + SvxBorderLineWidth::Thin }; bool sw_frmitems_parseXMLBorder( const OUString& rValue, 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/dbui/dbinsdlg.cxx b/sw/source/ui/dbui/dbinsdlg.cxx index 4ead5f41f49b..64fd72b9a253 100644 --- a/sw/source/ui/dbui/dbinsdlg.cxx +++ b/sw/source/ui/dbui/dbinsdlg.cxx @@ -1290,7 +1290,7 @@ void SwInsertDBColAutoPilot::DataToDoc( const Sequence<Any>& rSelection, } pField->SetInitialized(); - rSh.Insert( *pField ); + rSh.InsertField2( *pField ); } break; @@ -1354,7 +1354,7 @@ void SwInsertDBColAutoPilot::DataToDoc( const Sequence<Any>& rSelection, break; if( m_xRbAsField->get_active() ) - rSh.Insert( aNxtDBField ); + rSh.InsertField2( aNxtDBField ); if( !rSh.IsSttPara() ) rSh.SwEditShell::SplitNode(); diff --git a/sw/source/ui/fldui/flddb.cxx b/sw/source/ui/fldui/flddb.cxx index 01b9065cd36e..937c2b265c71 100644 --- a/sw/source/ui/fldui/flddb.cxx +++ b/sw/source/ui/fldui/flddb.cxx @@ -299,12 +299,18 @@ void SwFieldDBPage::TypeHdl(const weld::TreeView* pBox) OUString sColumnName; if (nTypeId == SwFieldTypesEnum::Database) { - aData = static_cast<SwDBField*>(GetCurField())->GetDBData(); - sColumnName = static_cast<SwDBFieldType*>(GetCurField()->GetTyp())->GetColumnName(); + if (auto const*const pField = dynamic_cast<SwDBField*>(GetCurField())) + { + aData = pField->GetDBData(); + sColumnName = static_cast<SwDBFieldType*>(GetCurField()->GetTyp())->GetColumnName(); + } } else { - aData = static_cast<SwDBNameInfField*>(GetCurField())->GetDBData(pSh->GetDoc()); + if (auto *const pField = dynamic_cast<SwDBNameInfField*>(GetCurField())) + { + aData = pField->GetDBData(pSh->GetDoc()); + } } m_xDatabaseTLB->Select(aData.sDataSource, aData.sCommand, sColumnName); } diff --git a/sw/source/ui/fldui/flddinf.cxx b/sw/source/ui/fldui/flddinf.cxx index 92d263a6efd1..4fe4d8b41aaa 100644 --- a/sw/source/ui/fldui/flddinf.cxx +++ b/sw/source/ui/fldui/flddinf.cxx @@ -99,10 +99,13 @@ void SwFieldDokInfPage::Reset(const SfxItemSet* ) if (IsFieldEdit()) { const SwField* pCurField = GetCurField(); - nSubType = static_cast<const SwDocInfoField*>(pCurField)->GetSubType() & 0xff; + nSubType = pCurField->GetSubType() & 0xff; if( nSubType == DI_CUSTOM ) { - m_sOldCustomFieldName = static_cast<const SwDocInfoField*>(pCurField)->GetName(); + if (auto const pField = dynamic_cast<SwDocInfoField const*>(pCurField)) + { + m_sOldCustomFieldName = pField->GetName(); + } } m_xFormatLB->SetAutomaticLanguage(pCurField->IsAutomaticLanguage()); SwWrtShell *pSh = GetWrtShell(); @@ -311,12 +314,17 @@ IMPL_LINK_NOARG(SwFieldDokInfPage, SubTypeHdl, weld::TreeView&, void) bEnable = true; } - sal_uInt32 nFormat = IsFieldEdit() ? static_cast<SwDocInfoField*>(GetCurField())->GetFormat() : 0; + sal_uInt32 nFormat = 0; - sal_uInt16 nOldSubType = IsFieldEdit() ? (static_cast<SwDocInfoField*>(GetCurField())->GetSubType() & 0xff00) : 0; + sal_uInt16 nOldSubType = 0; if (IsFieldEdit()) { + if (auto const pField = dynamic_cast<SwDocInfoField const*>(GetCurField())) + { + nFormat = pField->GetFormat(); + nOldSubType = pField->GetSubType() & 0xff00; + } nPos = m_xSelectionLB->get_selected_index(); if (nPos != -1) { @@ -367,10 +375,14 @@ sal_Int32 SwFieldDokInfPage::FillSelectionLB(sal_uInt16 nSubType) sal_uInt16 nSize = 0; sal_Int32 nSelPos = -1; - sal_uInt16 nExtSubType = IsFieldEdit() ? (static_cast<SwDocInfoField*>(GetCurField())->GetSubType() & 0xff00) : 0; + sal_uInt16 nExtSubType = 0; if (IsFieldEdit()) { + if (auto const pField = dynamic_cast<SwDocInfoField const*>(GetCurField())) + { + nExtSubType = pField->GetSubType() & 0xff00; + } m_xFixedCB->set_active((nExtSubType & DI_SUB_FIXED) != 0); nExtSubType = ((nExtSubType & ~DI_SUB_FIXED) >> 8) - 1; } diff --git a/sw/source/ui/fldui/fldref.cxx b/sw/source/ui/fldui/fldref.cxx index 95935b115177..d13ed61cd550 100644 --- a/sw/source/ui/fldui/fldref.cxx +++ b/sw/source/ui/fldui/fldref.cxx @@ -330,9 +330,15 @@ IMPL_LINK_NOARG(SwFieldRefPage, TypeHdl, weld::TreeView&, void) break; case REF_SEQUENCEFLD: - sName = static_cast<SwGetRefField*>(GetCurField())->GetSetRefName(); + { + SwGetRefField const*const pRefField(dynamic_cast<SwGetRefField*>(GetCurField())); + if (pRefField) + { + sName = pRefField->GetSetRefName(); + } nFlag = REFFLDFLAG; break; + } } if (m_xTypeLB->find_text(sName) == -1) // reference to deleted mark @@ -468,7 +474,7 @@ void SwFieldRefPage::UpdateSubType(const OUString& filterString) SwWrtShell *pSh = GetWrtShell(); if(!pSh) pSh = ::GetActiveWrtShell(); - SwGetRefField* pRefField = static_cast<SwGetRefField*>(GetCurField()); + SwGetRefField const*const pRefField(dynamic_cast<SwGetRefField*>(GetCurField())); const sal_uInt16 nTypeId = m_xTypeLB->get_id(GetTypeSel()).toUInt32(); OUString sOldSel; @@ -479,7 +485,7 @@ void SwFieldRefPage::UpdateSubType(const OUString& filterString) if (nSelectionSel != -1) sOldSel = m_xSelectionLB->get_text(nSelectionSel); } - if (IsFieldEdit() && sOldSel.isEmpty()) + if (IsFieldEdit() && pRefField && sOldSel.isEmpty()) sOldSel = OUString::number( pRefField->GetSeqNo() + 1 ); m_xSelectionLB->freeze(); @@ -528,7 +534,7 @@ void SwFieldRefPage::UpdateSubType(const OUString& filterString) } } } - if (IsFieldEdit()) + if (IsFieldEdit() && pRefField) sOldSel = pRefField->GetSetRefName(); } else if (nTypeId == REFFLDFLAG_FOOTNOTE) @@ -543,7 +549,7 @@ void SwFieldRefPage::UpdateSubType(const OUString& filterString) { m_xSelectionLB->append_text( aArr[ n ].sDlgEntry ); } - if (IsFieldEdit() && pRefField->GetSeqNo() == aArr[ n ].nSeqNo) + if (IsFieldEdit() && pRefField && pRefField->GetSeqNo() == aArr[ n ].nSeqNo) sOldSel = aArr[n].sDlgEntry; } } @@ -559,7 +565,7 @@ void SwFieldRefPage::UpdateSubType(const OUString& filterString) { m_xSelectionLB->append_text( aArr[ n ].sDlgEntry ); } - if (IsFieldEdit() && pRefField->GetSeqNo() == aArr[ n ].nSeqNo) + if (IsFieldEdit() && pRefField && pRefField->GetSeqNo() == aArr[ n ].nSeqNo) sOldSel = aArr[n].sDlgEntry; } } @@ -583,9 +589,9 @@ void SwFieldRefPage::UpdateSubType(const OUString& filterString) OUString sId(OUString::number(nOutlIdx)); m_xSelectionToolTipLB->append(sId, pIDoc->getOutlineText(nOutlIdx, pSh->GetLayout(), true, true, false)); - if ( ( IsFieldEdit() && - pRefField->GetReferencedTextNode() == maOutlineNodes[nOutlIdx] ) || - mpSavedSelectedTextNode == maOutlineNodes[nOutlIdx] ) + if ((IsFieldEdit() && pRefField + && pRefField->GetReferencedTextNode() == maOutlineNodes[nOutlIdx]) + || mpSavedSelectedTextNode == maOutlineNodes[nOutlIdx]) { m_sSelectionToolTipLBId = sId; sOldSel.clear(); @@ -618,9 +624,9 @@ void SwFieldRefPage::UpdateSubType(const OUString& filterString) OUString sId(OUString::number(nNumItemIdx)); m_xSelectionToolTipLB->append(sId, pIDoc->getListItemText(*maNumItems[nNumItemIdx], *pSh->GetLayout())); - if ( ( IsFieldEdit() && - pRefField->GetReferencedTextNode() == maNumItems[nNumItemIdx]->GetTextNode() ) || - mpSavedSelectedTextNode == maNumItems[nNumItemIdx]->GetTextNode() ) + if ((IsFieldEdit() && pRefField + && pRefField->GetReferencedTextNode() == maNumItems[nNumItemIdx]->GetTextNode()) + || mpSavedSelectedTextNode == maNumItems[nNumItemIdx]->GetTextNode()) { m_sSelectionToolTipLBId = sId; sOldSel.clear(); @@ -655,12 +661,12 @@ void SwFieldRefPage::UpdateSubType(const OUString& filterString) { m_xSelectionLB->append_text( aArr[ n ].sDlgEntry ); } - if (IsFieldEdit() && sOldSel.isEmpty() && + if (IsFieldEdit() && pRefField && sOldSel.isEmpty() && aArr[ n ].nSeqNo == pRefField->GetSeqNo()) sOldSel = aArr[ n ].sDlgEntry; } - if (IsFieldEdit() && sOldSel.isEmpty()) + if (IsFieldEdit() && pRefField && sOldSel.isEmpty()) sOldSel = OUString::number( pRefField->GetSeqNo() + 1); } } @@ -678,7 +684,7 @@ void SwFieldRefPage::UpdateSubType(const OUString& filterString) } } - if (IsFieldEdit()) + if (IsFieldEdit() && pRefField) sOldSel = pRefField->GetSetRefName(); } @@ -950,7 +956,7 @@ bool SwFieldRefPage::FillItemSet(SfxItemSet* ) } } - SwGetRefField* pRefField = static_cast<SwGetRefField*>(GetCurField()); + SwGetRefField const*const pRefField(dynamic_cast<SwGetRefField*>(GetCurField())); if (REFFLDFLAG & nTypeId) { @@ -980,10 +986,10 @@ bool SwFieldRefPage::FillItemSet(SfxItemSet* ) { aVal = OUString::number( aArr[nPos].nSeqNo ); - if (IsFieldEdit() && aArr[nPos].nSeqNo == pRefField->GetSeqNo()) + if (IsFieldEdit() && pRefField && aArr[nPos].nSeqNo == pRefField->GetSeqNo()) bModified = true; // can happen with fields of which the references were deleted } - else if (IsFieldEdit()) + else if (IsFieldEdit() && pRefField) aVal = OUString::number( pRefField->GetSeqNo() ); } else if (REFFLDFLAG_ENDNOTE == nTypeId) // endnotes @@ -1001,10 +1007,10 @@ bool SwFieldRefPage::FillItemSet(SfxItemSet* ) { aVal = OUString::number( aArr[nPos].nSeqNo ); - if (IsFieldEdit() && aArr[nPos].nSeqNo == pRefField->GetSeqNo()) + if (IsFieldEdit() && pRefField && aArr[nPos].nSeqNo == pRefField->GetSeqNo()) bModified = true; // can happen with fields of which the reference was deleted } - else if (IsFieldEdit()) + else if (IsFieldEdit() && pRefField) aVal = OUString::number( pRefField->GetSeqNo() ); } // #i83479# @@ -1069,10 +1075,10 @@ bool SwFieldRefPage::FillItemSet(SfxItemSet* ) { aVal = OUString::number( aArr[nPos].nSeqNo ); - if (IsFieldEdit() && aArr[nPos].nSeqNo == pRefField->GetSeqNo()) + if (IsFieldEdit() && pRefField && aArr[nPos].nSeqNo == pRefField->GetSeqNo()) bModified = true; // can happen with fields of which the reference was deleted } - else if (IsFieldEdit()) + else if (IsFieldEdit() && pRefField) aVal = OUString::number( pRefField->GetSeqNo() ); } } diff --git a/sw/source/ui/fldui/fldtdlg.cxx b/sw/source/ui/fldui/fldtdlg.cxx index 8c1bb998fb85..084c4d66c8c7 100644 --- a/sw/source/ui/fldui/fldtdlg.cxx +++ b/sw/source/ui/fldui/fldtdlg.cxx @@ -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(); + } } void SwFieldDlg::Initialize(SfxChildWinInfo const *pInfo) @@ -179,8 +186,9 @@ void SwFieldDlg::ReInitDlg() if(!pActiveView) return; const SwWrtShell& rSh = pActiveView->GetWrtShell(); - GetOKButton().set_sensitive(!rSh.IsReadOnlyAvailable() || - !rSh.HasReadonlySel()); + GetOKButton().set_sensitive(( !rSh.IsReadOnlyAvailable() + || !rSh.HasReadonlySel()) + && !SwCursorShell::PosInsideInputField(*rSh.GetCursor()->GetPoint())); ReInitTabPage("document"); ReInitTabPage("variables"); @@ -212,8 +220,10 @@ void SwFieldDlg::Activate() { bool bHtmlMode = (::GetHtmlMode(static_cast<SwDocShell*>(SfxObjectShell::Current())) & HTMLMODE_ON) != 0; const SwWrtShell& rSh = pView->GetWrtShell(); - GetOKButton().set_sensitive(!rSh.IsReadOnlyAvailable() || - !rSh.HasReadonlySel()); + GetOKButton().set_sensitive(( !rSh.IsReadOnlyAvailable() + || !rSh.HasReadonlySel()) + && !SwCursorShell::PosInsideInputField(*rSh.GetCursor()->GetPoint())); + ReInitTabPage("variables", true); @@ -233,9 +243,12 @@ void SwFieldDlg::EnableInsert(bool bEnable) OSL_ENSURE(pView, "no view found"); if( !pView || (pView->GetWrtShell().IsReadOnlyAvailable() && - pView->GetWrtShell().HasReadonlySel()) ) + pView->GetWrtShell().HasReadonlySel()) + || SwCursorShell::PosInsideInputField(*pView->GetWrtShell().GetCursor()->GetPoint())) + { bEnable = false; } + } GetOKButton().set_sensitive(bEnable); } diff --git a/sw/source/ui/misc/bookmark.cxx b/sw/source/ui/misc/bookmark.cxx index 7267f5a059f4..649c1e806e79 100644 --- a/sw/source/ui/misc/bookmark.cxx +++ b/sw/source/ui/misc/bookmark.cxx @@ -103,7 +103,7 @@ IMPL_LINK_NOARG(SwInsertBookmarkDlg, DeleteHdl, weld::Button&, void) sw::mark::IMark* pBookmark = reinterpret_cast<sw::mark::IMark*>(m_xBookmarksBox->get_id(rEntry).toInt64()); OUString sRemoved = pBookmark->GetName(); IDocumentMarkAccess* const pMarkAccess = rSh.getIDocumentMarkAccess(); - pMarkAccess->deleteMark(pMarkAccess->findMark(sRemoved)); + pMarkAccess->deleteMark(pMarkAccess->findMark(sRemoved), false); SfxRequest aReq(rSh.GetView().GetViewFrame(), FN_DELETE_BOOKMARK); aReq.AppendItem(SfxStringItem(FN_DELETE_BOOKMARK, sRemoved)); aReq.Done(); @@ -440,10 +440,10 @@ void BookmarkTable::InsertBookmark(sw::mark::IMark* pMark) else if (bPulling && !bPulledAll) sBookmarkNodeText = "..." + sBookmarkNodeText; + const OUString& sHideCondition = pBookmark->GetHideCondition(); OUString sHidden = SwResId(STR_BOOKMARK_NO); - if (pBookmark->IsHidden()) + if (pBookmark->IsHidden() || !sHideCondition.isEmpty()) sHidden = SwResId(STR_BOOKMARK_YES); - const OUString& sHideCondition = pBookmark->GetHideCondition(); OUString sPageNum = OUString::number(SwPaM(pMark->GetMarkStart()).GetPageNum()); int nRow = m_xControl->n_children(); m_xControl->append(OUString::number(reinterpret_cast<sal_Int64>(pMark)), sPageNum); 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/dochdl/swdtflvr.cxx b/sw/source/uibase/dochdl/swdtflvr.cxx index f6e5990b1b3d..244e95de182c 100644 --- a/sw/source/uibase/dochdl/swdtflvr.cxx +++ b/sw/source/uibase/dochdl/swdtflvr.cxx @@ -2580,7 +2580,7 @@ bool SwTransferable::PasteDDE( TransferableDataHelper& rData, { // insert SwDDEField aSwDDEField( pDDETyp ); - rWrtShell.Insert( aSwDDEField ); + rWrtShell.InsertField2( aSwDDEField ); } } while( false ); @@ -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 { @@ -4217,7 +4217,7 @@ bool SwTransferDdeLink::WriteData( SvStream& rStrm ) // remove mark rServerObject.SetNoServer(); // this removes the connection between SwServerObject and mark // N.B. ppMark was not loaded from file and cannot have xml:id - pMarkAccess->deleteMark(ppMark); + pMarkAccess->deleteMark(ppMark, false); // recreate as Bookmark ::sw::mark::IMark* const pNewMark = pMarkAccess->makeMark( @@ -4252,7 +4252,7 @@ void SwTransferDdeLink::Disconnect( bool bRemoveDataAdvise ) bool bIsModified = pDoc->getIDocumentState().IsModified(); IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess(); - pMarkAccess->deleteMark(pMarkAccess->findMark(sName)); + pMarkAccess->deleteMark(pMarkAccess->findMark(sName), false); if( !bIsModified ) pDoc->getIDocumentState().ResetModified(); diff --git a/sw/source/uibase/docvw/OverlayRanges.hxx b/sw/source/uibase/docvw/OverlayRanges.hxx index 7482deef8f97..8deecac241fd 100644 --- a/sw/source/uibase/docvw/OverlayRanges.hxx +++ b/sw/source/uibase/docvw/OverlayRanges.hxx @@ -23,7 +23,9 @@ #include <svx/sdr/overlay/overlayobject.hxx> #include <basegfx/range/b2drange.hxx> +#include <memory> #include <vector> +#include <memory> class SwView; diff --git a/sw/source/uibase/docvw/ShadowOverlayObject.hxx b/sw/source/uibase/docvw/ShadowOverlayObject.hxx index ec2c7eaf44c1..506b801d0991 100644 --- a/sw/source/uibase/docvw/ShadowOverlayObject.hxx +++ b/sw/source/uibase/docvw/ShadowOverlayObject.hxx @@ -20,8 +20,14 @@ #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> + class SwView; namespace sw { namespace sidebarwindows { diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx index 80c00954fa0e..c6e5cbda9cbf 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; @@ -3698,7 +3701,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 @@ -3728,8 +3731,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 ); } } @@ -6285,8 +6288,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(); @@ -6295,8 +6297,7 @@ OUString SwEditWin::GetSurroundingText() const rSh.GoEndSentence(); rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR ); - pPos->nContent = nPos; - rSh.ClearMark(); + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); rSh.HideCursor(); } @@ -6316,18 +6317,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/fldui/fldmgr.cxx b/sw/source/uibase/fldui/fldmgr.cxx index 871cb366df1c..6bc001c99e0e 100644 --- a/sw/source/uibase/fldui/fldmgr.cxx +++ b/sw/source/uibase/fldui/fldmgr.cxx @@ -1494,40 +1494,49 @@ bool SwFieldMgr::InsertField( // insert pCurShell->StartAllAction(); - pCurShell->Insert(*pField, rData.m_pAnnotationRange.get()); + bool const isSuccess = pCurShell->InsertField2(*pField, rData.m_pAnnotationRange.get()); - if (SwFieldTypesEnum::Input == rData.m_nTypeId) + if (isSuccess) { - pCurShell->Push(); + if (SwFieldTypesEnum::Input == rData.m_nTypeId) + { + pCurShell->Push(); - // start dialog, not before the field is inserted tdf#99529 - pCurShell->Left(CRSR_SKIP_CHARS, false, - (INP_VAR == (nSubType & 0xff) || pCurShell->GetViewOptions()->IsFieldName()) ? 1 : 2, - false); - pCurShell->StartInputFieldDlg(pField.get(), false, true, rData.m_pParent); + // start dialog, not before the field is inserted tdf#99529 + pCurShell->Left(CRSR_SKIP_CHARS, false, + (INP_VAR == (nSubType & 0xff) || pCurShell->GetViewOptions()->IsFieldName()) ? 1 : 2, + false); + pCurShell->StartInputFieldDlg(pField.get(), false, true, rData.m_pParent); - pCurShell->Pop(SwCursorShell::PopMode::DeleteCurrent); - } + pCurShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + } - if(bExp && m_bEvalExp) - pCurShell->UpdateExpFields(true); + if (bExp && m_bEvalExp) + { + pCurShell->UpdateExpFields(true); + } - if(bTable) - { - pCurShell->Left(CRSR_SKIP_CHARS, false, 1, false ); - pCurShell->UpdateOneField(*pField); - pCurShell->Right(CRSR_SKIP_CHARS, false, 1, false ); + if (bTable) + { + pCurShell->Left(CRSR_SKIP_CHARS, false, 1, false ); + pCurShell->UpdateOneField(*pField); + pCurShell->Right(CRSR_SKIP_CHARS, false, 1, false ); + } + else if (bPageVar) + { + static_cast<SwRefPageGetFieldType*>(pCurShell->GetFieldType(0, SwFieldIds::RefPageGet))->UpdateFields(); + } + else if (SwFieldTypesEnum::GetRef == rData.m_nTypeId) + { + pField->GetTyp()->ModifyNotification( nullptr, nullptr ); + } } - else if( bPageVar ) - static_cast<SwRefPageGetFieldType*>(pCurShell->GetFieldType( 0, SwFieldIds::RefPageGet ))->UpdateFields(); - else if( SwFieldTypesEnum::GetRef == rData.m_nTypeId ) - pField->GetTyp()->ModifyNotification( nullptr, nullptr ); // delete temporary field pField.reset(); pCurShell->EndAllAction(); - return true; + return isSuccess; } // fields update diff --git a/sw/source/uibase/inc/wrtsh.hxx b/sw/source/uibase/inc/wrtsh.hxx index 6ec17d5b0e78..a61b049f5002 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(); @@ -300,7 +301,7 @@ typedef bool (SwWrtShell:: *FNSimpleMove)(); int IntelligentCut(SelectionType nSelectionType, bool bCut = true); // edit - void Insert(SwField const &, SwPaM* pAnnotationRange = nullptr); + bool InsertField2(SwField const &, SwPaM* pAnnotationRange = nullptr); void Insert(const OUString &); // graphic void Insert( const OUString &rPath, const OUString &rFilter, 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/shells/drwbassh.cxx b/sw/source/uibase/shells/drwbassh.cxx index 12d87bab691b..6f73e76ec91c 100644 --- a/sw/source/uibase/shells/drwbassh.cxx +++ b/sw/source/uibase/shells/drwbassh.cxx @@ -30,6 +30,7 @@ #include <svx/swframevalidation.hxx> #include <svx/anchorid.hxx> #include <sfx2/htmlmode.hxx> +#include <svx/hlnkitem.hxx> #include <drawdoc.hxx> #include <uitool.hxx> #include <fmtornt.hxx> @@ -54,16 +55,23 @@ #include <swslots.hxx> #include <svx/svxdlg.hxx> #include <svx/dialogs.hrc> +#include <vcl/unohelp2.hxx> #include <swabstdlg.hxx> #include <swundo.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/drawing/XShape.hpp> #include <com/sun/star/text/HoriOrientation.hpp> #include <com/sun/star/text/VertOrientation.hpp> #include <com/sun/star/text/RelOrientation.hpp> +#include <com/sun/star/uno/Reference.hxx> #include <IDocumentDrawModelAccess.hxx> #include <memory> #include <fmtfollowtextflow.hxx> using namespace ::com::sun::star; +using namespace css::beans; +using namespace css::drawing; +using namespace css::uno; SFX_IMPL_SUPERCLASS_INTERFACE(SwDrawBaseShell, SwBaseShell) @@ -603,6 +611,52 @@ void SwDrawBaseShell::Execute(SfxRequest const &rReq) break; } + case SID_OPEN_HYPERLINK: + { + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + LoadURL(GetShell(), pObj->getHyperlink(), LoadUrlFlags::NewView, + /*rTargetFrameName=*/OUString()); + break; + } + + case SID_EDIT_HYPERLINK: + case SID_HYPERLINK_DIALOG: + { + GetView().GetViewFrame()->SetChildWindow(SID_HYPERLINK_DIALOG, true); + break; + } + + case SID_HYPERLINK_SETLINK: + { + if(pItem) + { + const SvxHyperlinkItem& rHLinkItem = *static_cast<const SvxHyperlinkItem *>(pItem); + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + pObj->setHyperlink(rHLinkItem.GetURL()); + } + break; + } + + case SID_REMOVE_HYPERLINK: + { + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + pObj->setHyperlink(OUString()); + break; + } + + case SID_COPY_HYPERLINK_LOCATION: + { + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + uno::Reference<datatransfer::clipboard::XClipboard> xClipboard + = GetView().GetEditWin().GetClipboard(); + vcl::unohelper::TextDataObject::CopyStringTo(pObj->getHyperlink(), xClipboard); + break; + } + default: OSL_ENSURE(false, "wrong Dispatcher"); return; @@ -736,6 +790,62 @@ void SwDrawBaseShell::GetState(SfxItemSet& rSet) } } break; + + case SID_OPEN_HYPERLINK: + case SID_EDIT_HYPERLINK: + case SID_HYPERLINK_DIALOG: + case SID_REMOVE_HYPERLINK: + case SID_COPY_HYPERLINK_LOCATION: + { + if (pSdrView->GetMarkedObjectCount() != 1) + { + rSet.DisableItem(nWhich); + break; + } + + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + sal_uInt16 nObjType = pObj->GetObjIdentifier(); + + // Only enable hyperlink for the following types + switch (nObjType) + { + case OBJ_PATHFILL: + case OBJ_SECT: + case OBJ_LINE: + case OBJ_CUSTOMSHAPE: + case OBJ_TEXT: + case OBJ_RECT: + case OBJ_CAPTION: + case OBJ_POLY: + case OBJ_PLIN: + case OBJ_MEASURE: + case OBJ_EDGE: + break; + default: + rSet.DisableItem(nWhich); + break; + } + + if (nWhich == SID_OPEN_HYPERLINK || nWhich == SID_REMOVE_HYPERLINK + || nWhich == SID_EDIT_HYPERLINK || nWhich == SID_COPY_HYPERLINK_LOCATION) + { + if (pObj->getHyperlink().isEmpty()) + rSet.DisableItem(nWhich); + } + } + break; + + case SID_HYPERLINK_GETLINK: + { + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + OUString sHyperLink = pObj->getHyperlink(); + SvxHyperlinkItem aHLinkItem; + aHLinkItem.SetURL(sHyperLink); + rSet.Put(aHLinkItem); + } + break; } nWhich = aIter.NextWhich(); } 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/frmsh.cxx b/sw/source/uibase/shells/frmsh.cxx index 32036351a5ee..ba6e91f12ec1 100644 --- a/sw/source/uibase/shells/frmsh.cxx +++ b/sw/source/uibase/shells/frmsh.cxx @@ -1123,7 +1123,7 @@ void SwFrameShell::ExecFrameStyle(SfxRequest const & rReq) { aBorderLine.SetBorderLineStyle( SvxBorderLineStyle::SOLID); - aBorderLine.SetWidth( DEF_LINE_WIDTH_0 ); + aBorderLine.SetWidth( SvxBorderLineWidth::Hairline ); } //Set distance only if the request is received from the controller. diff --git a/sw/source/uibase/shells/tabsh.cxx b/sw/source/uibase/shells/tabsh.cxx index 073fe280c924..da77b89e4189 100644 --- a/sw/source/uibase/shells/tabsh.cxx +++ b/sw/source/uibase/shells/tabsh.cxx @@ -530,7 +530,7 @@ void SwTableShell::Execute(SfxRequest &rReq) if(aBorderLine.GetOutWidth() == 0) { aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID); - aBorderLine.SetWidth( DEF_LINE_WIDTH_5 ); + aBorderLine.SetWidth( SvxBorderLineWidth::VeryThin ); } if( aBox->GetTop() != nullptr ) diff --git a/sw/source/uibase/shells/textfld.cxx b/sw/source/uibase/shells/textfld.cxx index 3d6a2edee1f2..61f4b8e86f26 100644 --- a/sw/source/uibase/shells/textfld.cxx +++ b/sw/source/uibase/shells/textfld.cxx @@ -196,10 +196,10 @@ void SwTextShell::ExecField(SfxRequest &rReq) rSh.ClearMark(); if (!rSh.IsMultiSelection() && (nullptr != dynamic_cast<const SwTextInputField*>( - SwCursorShell::GetTextFieldAtCursor(rSh.GetCursor(), true)))) + SwCursorShell::GetTextFieldAtCursor(rSh.GetCursor(), ::sw::GetTextAttrMode::Default)))) { 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 3907f2611fac..ef5b86f6f199 100644 --- a/sw/source/uibase/shells/textsh1.cxx +++ b/sw/source/uibase/shells/textsh1.cxx @@ -708,7 +708,7 @@ void SwTextShell::Execute(SfxRequest &rReq) if ( pItem ) { IDocumentMarkAccess* const pMarkAccess = rWrtSh.getIDocumentMarkAccess(); - pMarkAccess->deleteMark( pMarkAccess->findMark(static_cast<const SfxStringItem*>(pItem)->GetValue()) ); + pMarkAccess->deleteMark(pMarkAccess->findMark(static_cast<const SfxStringItem*>(pItem)->GetValue()), false); } break; } 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/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx index 85e6d4559608..ad6a69df6c01 100644 --- a/sw/source/uibase/utlui/content.cxx +++ b/sw/source/uibase/utlui/content.cxx @@ -3532,7 +3532,7 @@ void SwContentTree::EditEntry(SvTreeListEntry const * pEntry, EditEntryMode nMod if(nMode == EditEntryMode::DELETE) { IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess(); - pMarkAccess->deleteMark( pMarkAccess->findMark(pCnt->GetName()) ); + pMarkAccess->deleteMark(pMarkAccess->findMark(pCnt->GetName()), false); } else if(nMode == EditEntryMode::RENAME) { diff --git a/sw/source/uibase/utlui/navipi.cxx b/sw/source/uibase/utlui/navipi.cxx index e016ab69910f..29bdd3938cde 100644 --- a/sw/source/uibase/utlui/navipi.cxx +++ b/sw/source/uibase/utlui/navipi.cxx @@ -512,7 +512,7 @@ void SwNavigationPI::MakeMark() // nAutoMarkIdx rotates through the available MarkNames // this assumes that IDocumentMarkAccess generates Names in ascending order if(vNavMarkNames.size() == MAX_MARKS) - pMarkAccess->deleteMark(pMarkAccess->findMark(vNavMarkNames[m_nAutoMarkIdx])); + pMarkAccess->deleteMark(pMarkAccess->findMark(vNavMarkNames[m_nAutoMarkIdx]), false); rSh.SetBookmark(vcl::KeyCode(), OUString(), IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER); SwView::SetActMark( m_nAutoMarkIdx ); diff --git a/sw/source/uibase/wrtsh/delete.cxx b/sw/source/uibase/wrtsh/delete.cxx index 4a2420ad7b84..8b5cd59bfa0c 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,14 +275,14 @@ bool SwWrtShell::DelLeft() } } } - bool bRet = Delete(); + bool bRet = Delete(true); if( !bRet && bSwap ) SwCursorShell::SwapPam(); CloseMark( bRet ); 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 @@ -309,7 +309,7 @@ bool SwWrtShell::DelRight() { SwActContext aActContext(this); ResetCursorStack(); - Delete(); + Delete(isReplaceHeuristic); UpdateAttr(); } if( IsBlockMode() ) @@ -392,7 +392,7 @@ bool SwWrtShell::DelRight() OpenMark(); SwCursorShell::Right(1, CRSR_SKIP_CELLS); - bRet = Delete(); + bRet = Delete(true); CloseMark( bRet ); break; @@ -434,7 +434,7 @@ bool SwWrtShell::DelRight() if (pTextNode) { const SwTextField* pField( - pTextNode->GetFieldTextAttrAt(pAnchor->nContent.GetIndex(), true)); + pTextNode->GetFieldTextAttrAt(pAnchor->nContent.GetIndex(), ::sw::GetTextAttrMode::Default)); if (pField && dynamic_cast<const SwPostItField*>(pField->GetFormatField().GetField())) { @@ -498,7 +498,7 @@ void SwWrtShell::DelToEndOfPara() Pop(SwCursorShell::PopMode::DeleteCurrent); return; } - bool bRet = Delete(); + bool bRet = Delete(false); Pop(SwCursorShell::PopMode::DeleteCurrent); if( bRet ) UpdateAttr(); @@ -515,7 +515,7 @@ void SwWrtShell::DelToStartOfPara() Pop(SwCursorShell::PopMode::DeleteCurrent); return; } - bool bRet = Delete(); + bool bRet = Delete(false); Pop(SwCursorShell::PopMode::DeleteCurrent); if( bRet ) UpdateAttr(); @@ -530,7 +530,7 @@ void SwWrtShell::DelToStartOfSentence() if(IsStartOfDoc()) return; OpenMark(); - bool bRet = BwdSentence_() && Delete(); + bool bRet = BwdSentence_() && Delete(false); CloseMark( bRet ); } @@ -562,7 +562,7 @@ bool SwWrtShell::DelToEndOfSentence() } else { - bRet = FwdSentence_() && Delete(); + bRet = FwdSentence_() && Delete(false); } CloseMark( bRet ); return bRet; @@ -583,7 +583,7 @@ void SwWrtShell::DelNxtWord() else EndWrd(); - bool bRet = Delete(); + bool bRet = Delete(false); if( bRet ) UpdateAttr(); else @@ -607,7 +607,7 @@ void SwWrtShell::DelPrvWord() else SttWrd(); } - bool bRet = Delete(); + bool bRet = Delete(false); if( bRet ) UpdateAttr(); else diff --git a/sw/source/uibase/wrtsh/select.cxx b/sw/source/uibase/wrtsh/select.cxx index 90664ae098f5..322199a2b1d5 100644 --- a/sw/source/uibase/wrtsh/select.cxx +++ b/sw/source/uibase/wrtsh/select.cxx @@ -914,7 +914,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 +928,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 0840ed3cd407..c240c630df3a 100644 --- a/sw/source/uibase/wrtsh/wrtsh1.cxx +++ b/sw/source/uibase/wrtsh/wrtsh1.cxx @@ -118,6 +118,9 @@ #include <comphelper/lok.hxx> #include <memory> +#include "../../core/crsr/callnk.hxx" + + using namespace sw::mark; using namespace com::sun::star; namespace { @@ -244,7 +247,8 @@ void SwWrtShell::Insert( const OUString &rStr ) StartUndo(SwUndoId::REPLACE, &aRewriter); bStarted = true; Push(); - bDeleted = DelRight(); + // 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(); @@ -1667,7 +1671,7 @@ void SwWrtShell::AutoCorrect( SvxAutoCorrect& rACorr, sal_Unicode cChar ) StartUndo( SwUndoId::REPLACE, &aRewriter ); bStarted = true; - DelRight(); + DelRight(true); } SwEditShell::AutoCorrect( rACorr, IsInsMode(), cChar ); @@ -1729,7 +1733,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; diff --git a/sw/source/uibase/wrtsh/wrtsh2.cxx b/sw/source/uibase/wrtsh/wrtsh2.cxx index 82d7c27cb0c6..459abd7f8b33 100644 --- a/sw/source/uibase/wrtsh/wrtsh2.cxx +++ b/sw/source/uibase/wrtsh/wrtsh2.cxx @@ -68,11 +68,11 @@ #include <sfx2/event.hxx> #include <sal/log.hxx> -void SwWrtShell::Insert(SwField const& rField, SwPaM* pAnnotationRange) +bool SwWrtShell::InsertField2(SwField const& rField, SwPaM* pAnnotationRange) { ResetCursorStack(); if(!CanInsert()) - return; + return false; StartAllAction(); SwRewriter aRewriter; @@ -120,7 +120,7 @@ void SwWrtShell::Insert(SwField const& rField, SwPaM* pAnnotationRange) } } - SwEditShell::Insert2(rField, bDeleted); + bool const isSuccess = SwEditShell::InsertField(rField, bDeleted); if ( pAnnotationTextRange ) { @@ -145,6 +145,8 @@ void SwWrtShell::Insert(SwField const& rField, SwPaM* pAnnotationRange) EndUndo(); EndAllAction(); + + return isSuccess; } // Start the field update @@ -488,30 +490,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 +522,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 +543,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 ) { |