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 0842c8c6eda1d459b2929deff11b35ed90580cb3
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Thu Mar 13 12:10:41 2025 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Fri Mar 14 08:11:23 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/+/182869
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182881

diff --git a/sw/inc/ndtxt.hxx b/sw/inc/ndtxt.hxx
index 30951a7d56bd..86c4ad1af06b 100644
--- a/sw/inc/ndtxt.hxx
+++ b/sw/inc/ndtxt.hxx
@@ -458,9 +458,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 dd16cff8c7a8..1ce054aab8a8 100644
--- a/sw/inc/node.hxx
+++ b/sw/inc/node.hxx
@@ -495,7 +495,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 1882602c8d93..4aaa42824c7a 100644
--- a/sw/qa/core/txtnode/txtnode.cxx
+++ b/sw/qa/core/txtnode/txtnode.cxx
@@ -549,6 +549,30 @@ CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, 
testPlainContentControlCopy)
     mxComponent.clear();
 }
 
+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 aebade8eb36f..1593ac584969 100644
--- a/sw/source/core/docnode/node.cxx
+++ b/sw/source/core/docnode/node.cxx
@@ -1261,7 +1261,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 00ad82c09d22..7f0c05d5dfbf 100644
--- a/sw/source/core/txtnode/ndtxt.cxx
+++ b/sw/source/core/txtnode/ndtxt.cxx
@@ -1656,7 +1656,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!
@@ -1666,7 +1667,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);
     }
@@ -3019,6 +3020,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 )
 {
@@ -3117,7 +3148,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;
 
@@ -3135,7 +3168,7 @@ SwTextNode* SwTextNode::MakeNewTextNode( SwNode& rPosNd, 
bool bNext,
             }
         }
     }
-    ChgFormatColl( pNextColl );
+    ChgFormatColl( pNextColl, bSetListLevel );
 
     return pNode;
 }
@@ -4098,7 +4131,7 @@ namespace {
     }
 }
 
-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." );
@@ -4125,7 +4158,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