sw/qa/core/frmedt/frmedt.cxx | 35 +++++++++++++++++++++++++++++ sw/source/core/frmedt/fefly1.cxx | 47 ++++++++++++++++++++++++++++++++++++++- sw/source/uibase/inc/frmmgr.hxx | 2 - 3 files changed, 82 insertions(+), 2 deletions(-)
New commits: commit f9f2b7590bb7b3334d499b6884cc7f3e80843b8c Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Apr 23 08:29:07 2024 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Tue Apr 23 09:48:01 2024 +0200 tdf#159379 sw: fix crash on dropping multiple as-char images Have an empty Writer document, set preferences so images created via drag&drop are anchored as-char, drop 2 images from a file manager, crash. The root of the problem is that the first image gets dropped fine, but the second one would be anchored to the currently selected graphic node, since commit 651527b4efe9700c8c8dff58ce5aa86ad5681f16 (sw: fix double-click opening frame dialog, not graphic dialog on images, 2022-04-26). The new SwTextCursor::GetModelPositionForViewPoint() returning a graphic node for a point inside the image looks correct, so fix the problem by extending SwFEShell::Insert() to take the anchor position as the anchor for the new image, in case a graphic node is selected. The original use-case would use SwEditWin::ExecuteDrop(), but keep the test simple and invoke the underlying SwFEShell::Insert() instead, that also triggers the problem. Change-Id: Ibba57aa28d0616ded16b4abb314f04974f1b8f9a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166499 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/qa/core/frmedt/frmedt.cxx b/sw/qa/core/frmedt/frmedt.cxx index b2a53e60db27..37425c13060e 100644 --- a/sw/qa/core/frmedt/frmedt.cxx +++ b/sw/qa/core/frmedt/frmedt.cxx @@ -250,6 +250,41 @@ CPPUNIT_TEST_FIXTURE(SwCoreFrmedtTest, testSplitFlyUnfloat) CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pDoc->GetTableFrameFormatCount(/*bUsed=*/true)); } +CPPUNIT_TEST_FIXTURE(SwCoreFrmedtTest, testInsertOnGrfNodeAsChar) +{ + // Given a selected as-char image: + createSwDoc(); + SwDoc* pDoc = getSwDocShell()->GetDoc(); + SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); + { + SfxItemSet aFrameSet(pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1>); + SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR); + aFrameSet.Put(aAnchor); + Graphic aGrf; + pWrtShell->SwFEShell::Insert(OUString(), OUString(), &aGrf, &aFrameSet); + } + + // When inserting another as-char image: + SfxItemSet aFrameSet(pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1>); + SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR); + aFrameSet.Put(aAnchor); + Graphic aGrf; + // Without the accompanying fix in place, this call crashed, we try to set a graphic node as an + // anchor of an as-char image (which should be a text node). + pWrtShell->SwFEShell::Insert(OUString(), OUString(), &aGrf, &aFrameSet); + + // Then make sure that the anchor of the second image is next to the first anchor: + CPPUNIT_ASSERT(pDoc->GetSpzFrameFormats()); + sw::FrameFormats<sw::SpzFrameFormat*>& rFormats = *pDoc->GetSpzFrameFormats(); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rFormats.size()); + const sw::SpzFrameFormat& rFormat1 = *rFormats[0]; + const SwPosition* pAnchor1 = rFormat1.GetAnchor().GetContentAnchor(); + const sw::SpzFrameFormat& rFormat2 = *rFormats[1]; + const SwPosition* pAnchor2 = rFormat2.GetAnchor().GetContentAnchor(); + CPPUNIT_ASSERT_EQUAL(pAnchor1->nNode, pAnchor2->nNode); + CPPUNIT_ASSERT_EQUAL(pAnchor1->GetContentIndex() + 1, pAnchor2->GetContentIndex()); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/frmedt/fefly1.cxx b/sw/source/core/frmedt/fefly1.cxx index 494b08b1c77c..a67d0753c97d 100644 --- a/sw/source/core/frmedt/fefly1.cxx +++ b/sw/source/core/frmedt/fefly1.cxx @@ -890,6 +890,43 @@ const SwFrameFormat *SwFEShell::NewFlyFrame( const SfxItemSet& rSet, bool bAnchV return pRet; } +namespace +{ +/// If pCursor points to an as-char anchored graphic node, then set the node's anchor position on +/// pAnchor and rPam. +bool SetAnchorOnGrfNodeForAsChar(SwShellCursor *pCursor, SwFormatAnchor* pAnchor, std::optional<SwPaM>& rPam) +{ + const SwPosition* pPoint = pCursor->GetPoint(); + if (pAnchor->GetAnchorId() != RndStdIds::FLY_AS_CHAR) + { + return false; + } + + if (!pPoint->GetNode().IsGrfNode()) + { + return false; + } + + SwFrameFormat* pFrameFormat = pPoint->GetNode().GetFlyFormat(); + if (!pFrameFormat) + { + return false; + } + + const SwPosition* pContentAnchor = pFrameFormat->GetAnchor().GetContentAnchor(); + if (!pContentAnchor) + { + return false; + } + + SwPosition aPosition(*pContentAnchor); + ++aPosition.nContent; + pAnchor->SetAnchor(&aPosition); + rPam.emplace(aPosition); + return true; +} +} + void SwFEShell::Insert( const OUString& rGrfName, const OUString& rFltName, const Graphic* pGraphic, const SfxItemSet* pFlyAttrSet ) @@ -905,6 +942,7 @@ void SwFEShell::Insert( const OUString& rGrfName, const OUString& rFltName, break; // Has the anchor not been set or been set incompletely? + std::optional<SwPaM> oPam; if( pFlyAttrSet ) { if( const SwFormatAnchor* pItem = pFlyAttrSet->GetItemIfSet( RES_ANCHOR, false ) ) @@ -917,6 +955,13 @@ void SwFEShell::Insert( const OUString& rGrfName, const OUString& rFltName, case RndStdIds::FLY_AS_CHAR: if( !pAnchor->GetAnchorNode() ) { + if (SetAnchorOnGrfNodeForAsChar(pCursor, pAnchor, oPam)) + { + // Don't anchor the image on the previous image, rather insert them next + // to each other. + break; + } + pAnchor->SetAnchor( pCursor->GetPoint() ); } break; @@ -940,7 +985,7 @@ void SwFEShell::Insert( const OUString& rGrfName, const OUString& rFltName, } } pFormat = GetDoc()->getIDocumentContentOperations().InsertGraphic( - *pCursor, rGrfName, + oPam.has_value() ? *oPam : *pCursor, rGrfName, rFltName, pGraphic, pFlyAttrSet, nullptr, nullptr ); diff --git a/sw/source/uibase/inc/frmmgr.hxx b/sw/source/uibase/inc/frmmgr.hxx index ed77c2322bfb..c574ac05009c 100644 --- a/sw/source/uibase/inc/frmmgr.hxx +++ b/sw/source/uibase/inc/frmmgr.hxx @@ -73,7 +73,7 @@ public: //CopyCtor for dialogs to check the metrics SW_DLLPUBLIC SwFlyFrameAttrMgr( bool bNew, SwWrtShell *pSh, SfxItemSet aSet ); - void SetAnchor(RndStdIds eId); + SW_DLLPUBLIC void SetAnchor(RndStdIds eId); inline RndStdIds GetAnchor() const; void SetHorzOrientation(sal_Int16 eOrient);