sw/qa/core/frmedt/frmedt.cxx     |   35 +++++++++++++++++++++++++++++
 sw/source/core/frmedt/fefly1.cxx |   47 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 81 insertions(+), 1 deletion(-)

New commits:
commit d7f6f7bd0dd5428bd1fd483dd13102174a19cd8e
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Tue Apr 23 08:29:07 2024 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Tue Apr 30 09:17:24 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>
    (cherry picked from commit f9f2b7590bb7b3334d499b6884cc7f3e80843b8c)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166511
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>
    (cherry picked from commit 008b1c3a8652b33b9b42ca0794a21ce9754e96f2)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166884
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.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 93ad4212cf65..2e5b8bf53d15 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 );

Reply via email to