sw/qa/extras/layout/data/three_sections.fodt |   18 ++++++
 sw/qa/extras/layout/layout2.cxx              |   43 ++++++++++++++
 sw/source/core/inc/sectfrm.hxx               |    2 
 sw/source/core/layout/frmtool.cxx            |   69 ++++++++---------------
 sw/source/core/layout/layhelp.hxx            |    3 +
 sw/source/core/layout/sectfrm.cxx            |   79 ++++++++++++++-------------
 6 files changed, 132 insertions(+), 82 deletions(-)

New commits:
commit 534b7da3cbfd4a5378bd42f94901d602f8ae51fd
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Thu Apr 20 11:18:34 2023 +0300
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Mon Apr 24 09:12:03 2023 +0200

    tdf#154113: do not forget to split the outermost section frame
    
    ... when the inserted section node is not the first one.
    
    The very first frame, where InsertCnt_ puts content to, may already have
    some content after the insertion position. When an inner section ends,
    the current section needs a new frame, and the rest of content must go
    to that new frame. Previously, the new empty frame was created without
    taking the content move into account.
    
    This moves the split into the single place inside InsertCnt_, to avoid
    special processing in MakeFrames.
    
    Change-Id: I1335ebbc620af0f2b064141e8267e5bd1af0b195
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150675
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    (cherry picked from commit efb3c57851d29440ef086c68a6c1ddbb8bc8fc00)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150713
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>

diff --git a/sw/qa/extras/layout/data/three_sections.fodt 
b/sw/qa/extras/layout/data/three_sections.fodt
new file mode 100644
index 000000000000..9233fed89085
--- /dev/null
+++ b/sw/qa/extras/layout/data/three_sections.fodt
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:body>
+  <office:text>
+   <text:p>Select the text below, copy to clipboard, and paste from clipboard, 
replacing the selection.</text:p>
+   <text:section text:name="Section1">
+    <text:p>&lt;-- Start selection here. Section1</text:p>
+   </text:section>
+   <text:section text:name="Section2">
+    <text:p>Section2</text:p>
+   </text:section>
+   <text:section text:name="Section3">
+    <text:p>Section3. End selection here --&gt;</text:p>
+   </text:section>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/sw/qa/extras/layout/layout2.cxx b/sw/qa/extras/layout/layout2.cxx
index 1e72bd335227..e3c83c0fb13e 100644
--- a/sw/qa/extras/layout/layout2.cxx
+++ b/sw/qa/extras/layout/layout2.cxx
@@ -18,6 +18,7 @@
 #include <unotools/syslocaleoptions.hxx>
 #include <editeng/unolingu.hxx>
 #include <o3tl/string_view.hxx>
+#include <vcl/scheduler.hxx>
 
 #include <unotxdoc.hxx>
 #include <rootfrm.hxx>
@@ -2744,6 +2745,48 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf153136)
     // CPPUNIT_ASSERT_GREATER(large, height);
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf154113)
+{
+    createSwDoc("three_sections.fodt");
+    Scheduler::ProcessEventsToIdle();
+
+    dispatchCommand(mxComponent, ".uno:GoToStartOfDoc", {});
+    dispatchCommand(mxComponent, ".uno:GoToNextPara", {});
+    dispatchCommand(mxComponent, ".uno:EndOfDocumentSel", {}); // to the end 
of current section!
+    dispatchCommand(mxComponent, ".uno:EndOfDocumentSel", {}); // to the end 
of the document.
+
+    uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY_THROW);
+    uno::Reference<container::XIndexAccess> 
xSelected(xModel->getCurrentSelection(),
+                                                      uno::UNO_QUERY_THROW);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSelected->getCount());
+    uno::Reference<text::XTextRange> xRange(xSelected->getByIndex(0), 
uno::UNO_QUERY_THROW);
+    CPPUNIT_ASSERT_EQUAL(OUString("<-- Start selection here. Section1" 
SAL_NEWLINE_STRING
+                                  "Section2" SAL_NEWLINE_STRING "Section3. End 
selection here -->"),
+                         xRange->getString());
+
+    dispatchCommand(mxComponent, ".uno:Cut", {});
+
+    xSelected.set(xModel->getCurrentSelection(), uno::UNO_QUERY_THROW);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSelected->getCount());
+    xRange.set(xSelected->getByIndex(0), uno::UNO_QUERY_THROW);
+    CPPUNIT_ASSERT_EQUAL(OUString(), xRange->getString());
+
+    dispatchCommand(mxComponent, ".uno:Paste", {});
+
+    xmlDocUniquePtr pXml = parseLayoutDump();
+
+    // Without the fix in place, this would fail with
+    // - Expected: 3
+    // - Actual  : 2
+    assertXPath(pXml, "/root/page/body/section", 3);
+    assertXPath(pXml, 
"/root/page/body/section[1]/txt/SwParaPortion/SwLineLayout", "portion",
+                "<-- Start selection here. Section1");
+    assertXPath(pXml, 
"/root/page/body/section[2]/txt/SwParaPortion/SwLineLayout", "portion",
+                "Section2");
+    assertXPath(pXml, 
"/root/page/body/section[3]/txt/SwParaPortion/SwLineLayout", "portion",
+                "Section3. End selection here -->");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/sectfrm.hxx b/sw/source/core/inc/sectfrm.hxx
index 6c40c6589f31..e08a9b0a7137 100644
--- a/sw/source/core/inc/sectfrm.hxx
+++ b/sw/source/core/inc/sectfrm.hxx
@@ -109,7 +109,7 @@ public:
      * Splits the SectionFrame surrounding the pFrame up in two parts:
      * pFrame and the start of the 2nd part
      */
-    bool SplitSect( SwFrame* pFrame, bool bAfter );
+    SwSectionFrame* SplitSect( SwFrame* pFrameStartAfter, SwFrame* 
pFramePutAfter );
     void DelEmpty( bool bRemove ); // Like Cut(), except for that Follow 
chaining is maintained
     SwFootnoteContFrame* ContainsFootnoteCont( const SwFootnoteContFrame* 
pCont = nullptr ) const;
     bool Growable() const;
diff --git a/sw/source/core/layout/frmtool.cxx 
b/sw/source/core/layout/frmtool.cxx
index b4f676b85745..f477400aea49 100644
--- a/sw/source/core/layout/frmtool.cxx
+++ b/sw/source/core/layout/frmtool.cxx
@@ -1757,6 +1757,9 @@ void InsertCnt_( SwLayoutFrame *pLay, SwDoc *pDoc,
                 nIndex = pNode->EndOfSectionIndex();
             else
             {
+                if (pActualSection)
+                    pActualSection->SetLastPos(pPrv);
+
                 pFrame = pNode->MakeFrame( pLay );
                 pActualSection.reset( new SwActualSection( 
pActualSection.release(),
                                                 
static_cast<SwSectionFrame*>(pFrame), pNode ) );
@@ -1913,33 +1916,30 @@ void InsertCnt_( SwLayoutFrame *pLay, SwDoc *pDoc,
                 }
 
                 // new section frame
-                pFrame = pActualSection->GetSectionNode()->MakeFrame( pLay );
-                pFrame->InsertBehind( pLay, pPrv );
-                static_cast<SwSectionFrame*>(pFrame)->Init();
-
-                // OD 12.08.2003 #i17969# - consider horizontal/vertical layout
-                // for setting position at newly inserted frame
-                lcl_SetPos( *pFrame, *pLay );
-
-                SwSectionFrame* pOuterSectionFrame = 
pActualSection->GetSectionFrame();
-
-                // a follow has to be appended to the new section frame
-                SwSectionFrame* pFollow = pOuterSectionFrame ? 
pOuterSectionFrame->GetFollow() : nullptr;
-                if ( pFollow )
+                if (SwSectionFrame* pOuterSectionFrame = 
pActualSection->GetSectionFrame())
                 {
-                    pOuterSectionFrame->SetFollow( nullptr );
-                    pOuterSectionFrame->InvalidateSize();
-                    static_cast<SwSectionFrame*>(pFrame)->SetFollow( pFollow );
-                }
+                    // Splitting moves the trailing content to the next frame
+                    pFrame = 
pOuterSectionFrame->SplitSect(pActualSection->GetLastPos(), pPrv);
 
-                // We don't want to leave empty parts back.
-                if (pOuterSectionFrame &&
-                    ! pOuterSectionFrame->IsColLocked() &&
-                    ! pOuterSectionFrame->ContainsContent() )
+                    // We don't want to leave empty parts back.
+                    if (! pOuterSectionFrame->IsColLocked() &&
+                        ! pOuterSectionFrame->ContainsContent() )
+                    {
+                        pOuterSectionFrame->DelEmpty( true );
+                        SwFrame::DestroyFrame(pOuterSectionFrame);
+                    }
+                }
+                else
                 {
-                    pOuterSectionFrame->DelEmpty( true );
-                    SwFrame::DestroyFrame(pOuterSectionFrame);
+                    pFrame = pActualSection->GetSectionNode()->MakeFrame( pLay 
);
+                    pFrame->InsertBehind( pLay, pPrv );
+                    static_cast<SwSectionFrame*>(pFrame)->Init();
+
+                    // OD 12.08.2003 #i17969# - consider horizontal/vertical 
layout
+                    // for setting position at newly inserted frame
+                    lcl_SetPos( *pFrame, *pLay );
                 }
+
                 pActualSection->SetSectionFrame( 
static_cast<SwSectionFrame*>(pFrame) );
 
                 pLay = static_cast<SwLayoutFrame*>(pFrame);
@@ -2167,20 +2167,7 @@ void MakeFrames( SwDoc *pDoc, SwNode &rSttIdx, SwNode 
&rEndIdx )
             }
             else
             {
-                bool bSplit;
                 SwFrame* pPrv = bAfter ? pFrame : pFrame->GetPrev();
-                // If the section frame is inserted into another one, it must 
be split.
-                if( pSct && rSttIdx.IsSectionNode() )
-                {
-                    bSplit = pSct->SplitSect( pFrame, bAfter );
-                    if( !bSplit && !bAfter )
-                    {
-                        pUpper = pSct->GetUpper();
-                        pPrv = pSct->GetPrev();
-                    }
-                }
-                else
-                    bSplit = false;
 
                 ::InsertCnt_( pUpper, pDoc, rSttIdx.GetIndex(), false,
                               nEndIdx, pPrv, eMode );
@@ -2193,10 +2180,6 @@ void MakeFrames( SwDoc *pDoc, SwNode &rSttIdx, SwNode 
&rEndIdx )
                         AppendAllObjs( pTable, pUpper );
                 }
 
-                // If nothing was added (e.g. a hidden section), the split 
must be reversed.
-                if( bSplit && pSct && pSct->GetNext()
-                    && pSct->GetNext()->IsSctFrame() )
-                    pSct->MergeNext( 
static_cast<SwSectionFrame*>(pSct->GetNext()) );
                 if( pFrame->IsInFly() )
                     pFrame->FindFlyFrame()->Invalidate_();
                 if( pFrame->IsInTab() )
diff --git a/sw/source/core/layout/layhelp.hxx 
b/sw/source/core/layout/layhelp.hxx
index 17c5cc32491e..02ff3ca4f327 100644
--- a/sw/source/core/layout/layhelp.hxx
+++ b/sw/source/core/layout/layhelp.hxx
@@ -86,6 +86,7 @@ class SwActualSection
 {
     SwActualSection *m_pUpper;
     SwSectionFrame    *m_pSectFrame;
+    SwFrame* m_pLastPos = nullptr; // Split it *after* this child frame
     SwSectionNode   *m_pSectNode;
 public:
     SwActualSection( SwActualSection *pUpper,
@@ -97,6 +98,8 @@ public:
     SwSectionNode   *GetSectionNode()                   { return m_pSectNode;}
     void             SetUpper(SwActualSection *p)       { m_pUpper = p; }
     SwActualSection *GetUpper()                         { return m_pUpper; }
+    void SetLastPos(SwFrame* p) { m_pLastPos = p; }
+    SwFrame* GetLastPos() const { return m_pLastPos; }
 };
 
 /// Helps during the InsertCnt_ function to create new pages.
diff --git a/sw/source/core/layout/sectfrm.cxx 
b/sw/source/core/layout/sectfrm.cxx
index 0a82b8cc0dfd..cc6c3982d3a4 100644
--- a/sw/source/core/layout/sectfrm.cxx
+++ b/sw/source/core/layout/sectfrm.cxx
@@ -511,50 +511,53 @@ void SwSectionFrame::MergeNext( SwSectionFrame* pNxt )
 }
 
 /**
-|*  Divides a SectionFrame into two parts. The second one starts with the
-|*  passed frame.
+|*  Divides a SectionFrame into two parts. The content of the second one
+|*  starts after pFrameStartAfter; the created second section frame itself
+|*  is put after pFramePutAfter.
+|*  If pFrameStartAfter is nullptr, the split happens at the start.
 |*  This is required when inserting an inner section, because the MoveFwd
 |*  cannot have the desired effect within a frame or a table cell.
+|*  Splitting at the start/end makes sense, because the empty frame would
+|*  be removed after the InsertCnt_ finished.
 |*/
-bool SwSectionFrame::SplitSect( SwFrame* pFrame, bool bAfter )
+SwSectionFrame* SwSectionFrame::SplitSect( SwFrame* pFrameStartAfter, SwFrame* 
pFramePutAfter )
 {
-    assert(pFrame && "SplitSect: Why?");
-    SwFrame* pOther = bAfter ? pFrame->FindNext() : pFrame->FindPrev();
-    if( !pOther )
-        return false;
-    SwSectionFrame* pSect = pOther->FindSctFrame();
-    if( pSect != this )
-        return false;
+    assert(!pFrameStartAfter || pFrameStartAfter->FindSctFrame() == this);
+    SwFrame* pSav = pFrameStartAfter ? pFrameStartAfter->FindNext() : 
ContainsAny();
+    if (pSav && pSav->FindSctFrame() != this)
+        pSav = nullptr; // we are at the very end
+
     // Put the content aside
-    SwFrame* pSav = ::SaveContent( this, bAfter ? pOther : pFrame );
-    OSL_ENSURE( pSav, "SplitSect: What's on?" );
-    if( pSav ) // be robust
-    {   // Create a new SctFrame, not as a Follower/master
-        SwSectionFrame* pNew = new SwSectionFrame( *pSect->GetSection(), pSect 
);
-        pNew->InsertBehind( pSect->GetUpper(), pSect );
-        pNew->Init();
-        SwRectFnSet aRectFnSet(this);
-        aRectFnSet.MakePos( *pNew, nullptr, pSect, true );
-        // OD 25.03.2003 #108339# - restore content:
-        // determine layout frame for restoring content after the 
initialization
-        // of the section frame. In the section initialization the columns are
-        // created.
-        {
-            SwLayoutFrame* pLay = pNew;
-            // Search for last layout frame, e.g. for columned sections.
-            while( pLay->Lower() && pLay->Lower()->IsLayoutFrame() )
-                pLay = static_cast<SwLayoutFrame*>(pLay->Lower());
-            ::RestoreContent( pSav, pLay, nullptr );
-        }
-        InvalidateSize_();
-        if( HasFollow() )
-        {
-            pNew->SetFollow( GetFollow() );
-            SetFollow( nullptr );
-        }
-        return true;
+    if (pSav)
+        pSav = ::SaveContent( this, pSav );
+
+    // Create a new SctFrame, not as a Follower/master
+    if (!pFramePutAfter)
+        pFramePutAfter = this;
+    SwSectionFrame* pNew = new SwSectionFrame( *GetSection(), this );
+    pNew->InsertBehind( pFramePutAfter->GetUpper(), pFramePutAfter );
+    pNew->Init();
+    SwRectFnSet aRectFnSet(this);
+    aRectFnSet.MakePos( *pNew, nullptr, pFramePutAfter, true );
+    // OD 25.03.2003 #108339# - restore content:
+    // determine layout frame for restoring content after the initialization
+    // of the section frame. In the section initialization the columns are
+    // created.
+    if (pSav)
+    {
+        SwLayoutFrame* pLay = pNew;
+        // Search for last layout frame, e.g. for columned sections.
+        while( pLay->Lower() && pLay->Lower()->IsLayoutFrame() )
+            pLay = static_cast<SwLayoutFrame*>(pLay->Lower());
+        ::RestoreContent( pSav, pLay, nullptr );
+    }
+    InvalidateSize_();
+    if( HasFollow() )
+    {
+        pNew->SetFollow( GetFollow() );
+        SetFollow( nullptr );
     }
-    return false;
+    return pNew;
 }
 
 /**
commit cd48db52932998373cb267d7cd585737be03445c
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Tue Apr 18 15:46:26 2023 +0300
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Mon Apr 24 09:11:55 2023 +0200

    bApres -> bAfter
    
    Change-Id: Iac6965fa7695e9123b7861add6e4425bb31b79ca
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150574
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    (cherry picked from commit 77441b237db98d6577c8799c0c636839855c5c52)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150720
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>

diff --git a/sw/source/core/inc/sectfrm.hxx b/sw/source/core/inc/sectfrm.hxx
index 09c742f8da79..6c40c6589f31 100644
--- a/sw/source/core/inc/sectfrm.hxx
+++ b/sw/source/core/inc/sectfrm.hxx
@@ -109,7 +109,7 @@ public:
      * Splits the SectionFrame surrounding the pFrame up in two parts:
      * pFrame and the start of the 2nd part
      */
-    bool SplitSect( SwFrame* pFrame, bool bApres );
+    bool SplitSect( SwFrame* pFrame, bool bAfter );
     void DelEmpty( bool bRemove ); // Like Cut(), except for that Follow 
chaining is maintained
     SwFootnoteContFrame* ContainsFootnoteCont( const SwFootnoteContFrame* 
pCont = nullptr ) const;
     bool Growable() const;
diff --git a/sw/source/core/layout/frmtool.cxx 
b/sw/source/core/layout/frmtool.cxx
index e47c68283e33..b4f676b85745 100644
--- a/sw/source/core/layout/frmtool.cxx
+++ b/sw/source/core/layout/frmtool.cxx
@@ -2027,7 +2027,7 @@ void MakeFrames( SwDoc *pDoc, SwNode &rSttIdx, SwNode 
&rEndIdx )
                     pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
     if ( pNd )
     {
-        bool bApres = *pNd < rSttIdx;
+        bool bAfter = *pNd < rSttIdx;
         SwNode2Layout aNode2Layout( *pNd, rSttIdx.GetIndex() );
         sw::FrameMode eMode = sw::FrameMode::Existing;
         ::std::vector<SwFrame*> frames;
@@ -2075,7 +2075,7 @@ void MakeFrames( SwDoc *pDoc, SwNode &rSttIdx, SwNode 
&rEndIdx )
                 SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pMove );
                 assert(pTmp);
 
-                if ( bApres )
+                if ( bAfter )
                 {
                     // The rest of this page should be empty. Thus, the 
following one has to move to
                     // the next page (it might also be located in the 
following column).
@@ -2168,12 +2168,12 @@ void MakeFrames( SwDoc *pDoc, SwNode &rSttIdx, SwNode 
&rEndIdx )
             else
             {
                 bool bSplit;
-                SwFrame* pPrv = bApres ? pFrame : pFrame->GetPrev();
+                SwFrame* pPrv = bAfter ? pFrame : pFrame->GetPrev();
                 // If the section frame is inserted into another one, it must 
be split.
                 if( pSct && rSttIdx.IsSectionNode() )
                 {
-                    bSplit = pSct->SplitSect( pFrame, bApres );
-                    if( !bSplit && !bApres )
+                    bSplit = pSct->SplitSect( pFrame, bAfter );
+                    if( !bSplit && !bAfter )
                     {
                         pUpper = pSct->GetUpper();
                         pPrv = pSct->GetPrev();
diff --git a/sw/source/core/layout/sectfrm.cxx 
b/sw/source/core/layout/sectfrm.cxx
index eb667dd51fc5..0a82b8cc0dfd 100644
--- a/sw/source/core/layout/sectfrm.cxx
+++ b/sw/source/core/layout/sectfrm.cxx
@@ -516,17 +516,17 @@ void SwSectionFrame::MergeNext( SwSectionFrame* pNxt )
 |*  This is required when inserting an inner section, because the MoveFwd
 |*  cannot have the desired effect within a frame or a table cell.
 |*/
-bool SwSectionFrame::SplitSect( SwFrame* pFrame, bool bApres )
+bool SwSectionFrame::SplitSect( SwFrame* pFrame, bool bAfter )
 {
     assert(pFrame && "SplitSect: Why?");
-    SwFrame* pOther = bApres ? pFrame->FindNext() : pFrame->FindPrev();
+    SwFrame* pOther = bAfter ? pFrame->FindNext() : pFrame->FindPrev();
     if( !pOther )
         return false;
     SwSectionFrame* pSect = pOther->FindSctFrame();
     if( pSect != this )
         return false;
     // Put the content aside
-    SwFrame* pSav = ::SaveContent( this, bApres ? pOther : pFrame );
+    SwFrame* pSav = ::SaveContent( this, bAfter ? pOther : pFrame );
     OSL_ENSURE( pSav, "SplitSect: What's on?" );
     if( pSav ) // be robust
     {   // Create a new SctFrame, not as a Follower/master

Reply via email to