sw/inc/ndtxt.hxx                                        |    5 +
 sw/inc/node.hxx                                         |    2 
 sw/qa/core/txtnode/data/node-split-style-list-level.odt |binary
 sw/qa/core/txtnode/txtnode.cxx                          |   24 ++++++++
 sw/source/core/docnode/node.cxx                         |    2 
 sw/source/core/txtnode/ndtxt.cxx                        |   45 +++++++++++++---
 6 files changed, 68 insertions(+), 10 deletions(-)

New commits:
commit a8ff11cc84db2dc6a6aa97ac2be9c3bd882140ca
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Thu Mar 13 12:10:41 2025 +0100
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Thu Mar 13 14:10:04 2025 +0100

    tdf#165701 sw: fix unexpected list level change on inserting new bullet
    
    Go to the end of the 3rd paragraph of the bugdoc, press Enter, list
    level of both the old and new paragraphs change from 5 to 4,
    unexpectedly.
    
    It seems what happens is that the paragraph in question has a style
    which has its outline level set to 4. So once the node is split, the
    newly inserted node will host the old text (so need to set the style on
    it to the old style, which sets list level to the outline level from the
    style as a side effect) and the old node will host what looks like a new
    bullet point on the UI, so there we need to set the paragraph style to
    the follow style. This second set-style also drops the list level direct
    formatting and takes the outline level from the style.
    
    Fix the problem by keeping the old scenario (paragraph style with a
    different follow style) unchanged, since a different list level on an
    actual paragraph style change can be expected -- but avoid setting a
    list level when this is a simple node split with just the same style on
    both nodes. That's not seen as an explicit "set style" by the user, so
    changing the list level there is confusing.
    
    In fact the list level is fine after copying the text node's item set
    from the old node to the new node, just make sure that
    SwTextNode::ChgTextCollUpdateNum() doesn't set a new list level in the
    "effectively no style change" case. Make sure to not set a new list
    level at both places (SwTextNode::MakeNewTextNode() does this for the
    previous node, SwTextNode::ChgFormatColl() does this for the next node).
    
    Change-Id: Ifdf9048f01fef7bb8e99098ebeb5d1ec339fdaa6
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182861
    Tested-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>

diff --git a/sw/inc/ndtxt.hxx b/sw/inc/ndtxt.hxx
index 1024974c60b6..9d6b03ab6aa1 100644
--- a/sw/inc/ndtxt.hxx
+++ b/sw/inc/ndtxt.hxx
@@ -464,9 +464,10 @@ public:
     bool Convert( SwConversionArgs & );
 
     inline SwTextFormatColl *GetTextColl() const;
-    virtual SwFormatColl *ChgFormatColl( SwFormatColl* ) override;
+    virtual SwFormatColl *ChgFormatColl( SwFormatColl*, bool bSetListLevel = 
true ) override;
     void ChgTextCollUpdateNum( const SwTextFormatColl* pOld,
-                                const SwTextFormatColl* pNew );
+                                const SwTextFormatColl* pNew,
+                                bool bSetListLevel = true );
 
     /** Copy collection with all auto formats to dest-node.
         The latter might be in another document!
diff --git a/sw/inc/node.hxx b/sw/inc/node.hxx
index e42b6e80e53c..3708209b78f9 100644
--- a/sw/inc/node.hxx
+++ b/sw/inc/node.hxx
@@ -493,7 +493,7 @@ public:
     const SwAttrSet *GetpSwAttrSet() const { return mpAttrSet.get(); }
     bool  HasSwAttrSet() const { return mpAttrSet != nullptr; }
 
-    virtual SwFormatColl* ChgFormatColl( SwFormatColl* );
+    virtual SwFormatColl* ChgFormatColl( SwFormatColl*, bool bSetListLevel = 
true );
     SwFormatColl* GetFormatColl() const { return 
const_cast<SwFormatColl*>(static_cast<const SwFormatColl*>(GetRegisteredIn())); 
}
 
     inline SwFormatColl& GetAnyFormatColl() const;
diff --git a/sw/qa/core/txtnode/data/node-split-style-list-level.odt 
b/sw/qa/core/txtnode/data/node-split-style-list-level.odt
new file mode 100644
index 000000000000..f2535bb69b9c
Binary files /dev/null and 
b/sw/qa/core/txtnode/data/node-split-style-list-level.odt differ
diff --git a/sw/qa/core/txtnode/txtnode.cxx b/sw/qa/core/txtnode/txtnode.cxx
index e967e58436d6..428b7561d852 100644
--- a/sw/qa/core/txtnode/txtnode.cxx
+++ b/sw/qa/core/txtnode/txtnode.cxx
@@ -598,6 +598,30 @@ CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, 
testCopyCommentsWithReplies)
     CPPUNIT_ASSERT_EQUAL(comments[2]->GetName(), comments[3]->GetParentName());
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testNodeSplitStyleListLevel)
+{
+    // Given a document with a 3rd paragraph where the list level as direct 
formatting differs from
+    // the list level from style:
+    createSwDoc("node-split-style-list-level.odt");
+    SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+    pWrtShell->Down(/*bSelect=*/false, /*nCount=*/2);
+    pWrtShell->EndPara();
+
+    // When pressing enter at the end of the paragraph:
+    pWrtShell->SplitNode();
+
+    SwTextNode* pNext = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 4
+    // - Actual  : 3
+    // i.e. the list level for the new paragraph changed on a simple node 
split.
+    CPPUNIT_ASSERT_EQUAL(4, pNext->GetAttrListLevel());
+    pWrtShell->Up(/*bSelect=*/false, /*nCount=*/1);
+    SwTextNode* pPrevious = 
pWrtShell->GetCursor()->GetPointNode().GetTextNode();
+    // Same happened for the old paragraph.
+    CPPUNIT_ASSERT_EQUAL(4, pPrevious->GetAttrListLevel());
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/node.cxx b/sw/source/core/docnode/node.cxx
index ca38e625a0e4..957d6a2cb10f 100644
--- a/sw/source/core/docnode/node.cxx
+++ b/sw/source/core/docnode/node.cxx
@@ -1258,7 +1258,7 @@ SwRect SwContentNode::FindPageFrameRect() const
 
 sal_Int32 SwContentNode::Len() const { return 0; }
 
-SwFormatColl *SwContentNode::ChgFormatColl( SwFormatColl *pNewColl )
+SwFormatColl *SwContentNode::ChgFormatColl( SwFormatColl *pNewColl, bool 
/*bSetListLevel*/ )
 {
     OSL_ENSURE( pNewColl, "Collectionpointer is 0." );
     SwFormatColl *pOldColl = GetFormatColl();
diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx
index 458d1e99f637..ec2ad33d28c5 100644
--- a/sw/source/core/txtnode/ndtxt.cxx
+++ b/sw/source/core/txtnode/ndtxt.cxx
@@ -1666,7 +1666,8 @@ void SwTextNode::Update(
 }
 
 void SwTextNode::ChgTextCollUpdateNum( const SwTextFormatColl *pOldColl,
-                                        const SwTextFormatColl *pNewColl)
+                                        const SwTextFormatColl *pNewColl,
+                                        bool bSetListLevel)
 {
     SwDoc& rDoc = GetDoc();
     // query the OutlineLevel and if it changed, notify the Nodes-Array!
@@ -1675,7 +1676,7 @@ void SwTextNode::ChgTextCollUpdateNum( const 
SwTextFormatColl *pOldColl,
     const int nNewLevel = pNewColl && 
pNewColl->IsAssignedToListLevelOfOutlineStyle() ?
                      pNewColl->GetAssignedOutlineStyleLevel() : MAXLEVEL;
 
-    if ( MAXLEVEL != nNewLevel && -1 != nNewLevel )
+    if ( MAXLEVEL != nNewLevel && -1 != nNewLevel && bSetListLevel )
     {
         SetAttrListLevel(nNewLevel);
     }
@@ -3075,6 +3076,36 @@ bool SwTextNode::HasMarkedLabel() const
 }
 // <- #i27615#
 
+namespace
+{
+/// Decides if a list level direct formatting on a paragraph needs copying to 
a next, new paragraph.
+bool CopyDirectListLevel(SwTextNode* pTextNode)
+{
+    SwTextFormatColl* pColl = pTextNode->GetTextColl();
+    if (!pColl)
+    {
+        // No style, so can't have a conflict with a direct formatting.
+        return false;
+    }
+
+    if (&pColl->GetNextTextFormatColl() != pColl)
+    {
+        // Style has a custom follow style, changing list level is OK.
+        return false;
+    }
+
+    if (!pColl->IsAssignedToListLevelOfOutlineStyle())
+    {
+        // Paragraph style has no own list level, no conflict.
+        return false;
+    }
+
+    // Copy is needed if the old paragraph had a direct formatting, which may 
be different and has
+    // to be kept during the paragraph split.
+    return pTextNode->HasAttrListLevel();
+}
+}
+
 SwTextNode* SwTextNode::MakeNewTextNode( SwNode& rPosNd, bool bNext,
                                        bool bChgFollow )
 {
@@ -3173,7 +3204,9 @@ SwTextNode* SwTextNode::MakeNewTextNode( SwNode& rPosNd, 
bool bNext,
         ( bChgFollow && pColl != GetTextColl() ))
         return pNode;       // that ought to be enough?
 
-    pNode->ChgTextCollUpdateNum( nullptr, pColl ); // for numbering/outline
+    bool bSetListLevel = !CopyDirectListLevel(this);
+
+    pNode->ChgTextCollUpdateNum( nullptr, pColl, bSetListLevel ); // for 
numbering/outline
     if( bNext || !bChgFollow )
         return pNode;
 
@@ -3191,7 +3224,7 @@ SwTextNode* SwTextNode::MakeNewTextNode( SwNode& rPosNd, 
bool bNext,
             }
         }
     }
-    ChgFormatColl( pNextColl );
+    ChgFormatColl( pNextColl, bSetListLevel );
 
     return pNode;
 }
@@ -4135,7 +4168,7 @@ namespace {
     // End of method <HandleModifyAtTextNode>
 }
 
-SwFormatColl* SwTextNode::ChgFormatColl( SwFormatColl *pNewColl )
+SwFormatColl* SwTextNode::ChgFormatColl( SwFormatColl *pNewColl, bool 
bSetListLevel )
 {
     OSL_ENSURE( pNewColl,"ChgFormatColl: Collectionpointer has value 0." );
     assert( dynamic_cast<const SwTextFormatColl *>(pNewColl) && 
"ChgFormatColl: is not a Text Collection pointer." );
@@ -4164,7 +4197,7 @@ SwFormatColl* SwTextNode::ChgFormatColl( SwFormatColl 
*pNewColl )
     // only for real nodes-array
     if( GetNodes().IsDocNodes() )
     {
-        ChgTextCollUpdateNum( pOldColl, static_cast<SwTextFormatColl 
*>(pNewColl) );
+        ChgTextCollUpdateNum( pOldColl, static_cast<SwTextFormatColl 
*>(pNewColl), bSetListLevel );
     }
 
     return pOldColl;

Reply via email to