sd/CppunitTest_sd_ui_func.mk           |   88 +++++++++++++++++++++++++++++++++
 sd/Module_sd.mk                        |    1 
 sd/qa/ui/func/func.cxx                 |   88 +++++++++++++++++++++++++++++++++
 sd/qa/unit/data/odp/none-to-bullet.odp |binary
 sd/qa/unit/sdmodeltestbase.hxx         |   19 +++++++
 sd/qa/unit/uiimpress.cxx               |   19 -------
 sd/source/ui/func/fuolbull.cxx         |    8 ++-
 7 files changed, 203 insertions(+), 20 deletions(-)

New commits:
commit fe9fab9702613b1a5d192821d8a620aa527234b7
Author:     Miklos Vajna <[email protected]>
AuthorDate: Thu Dec 18 08:21:14 2025 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Thu Dec 18 13:34:33 2025 +0100

    Related: tdf#89365 sd UI, from numbering to bullet: fix defaults
    
    Open the bugdoc, start text edit on the only slide, in the only shape.
    Use the toolbar button to change from no numbering to bullet, some very
    small bullet with no spacing between the bullet and the text shows up.
    
    This happens since commit f60ee00edcc5b0fdee5227bb695448119cddb013
    (tdf#89365 sd UI: fix transitioning from a numbered list to a bulleted
    list, 2025-11-04), previously the numbering to bullet transitioning was
    broken and you had to click on the "turn on bullets" button twice. A
    side effect of the old two-step transitioning was that no formatting
    from the old numbering was kept, so better defaults were used (but the
    numbering level was lost).
    
    Keep the old use-case fixed and fix the new use-case by looking at how
    this works on master pages: there TextObjectBar::ExecuteImpl()'s
    FN_NUM_BULLET_ON block calls
    SdStyleSheetPool::setDefaultOutlineNumberFormatBulletAndIndent() before
    setting the number format for a specific level of the numbering rule. Do
    the same for non-master pages in
    FuBulletAndPosition::SetCurrentBulletsNumbering(): if we transition to a
    bullet, then use the same defaults in the non-master-page case, too.
    
    Note that turning bullets on or off as direct formatting is not ideal: a
    better style is to assign bullets (or no bullets) to different outline
    levels in the master page, and then just change the list level of
    paragraphs in the outliner shape, which avoids this sort of problem in
    the first place.
    
    Change-Id: I4227988593baeaa91cc21525fb895c3acaf3afd7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195839
    Reviewed-by: Miklos Vajna <[email protected]>
    Tested-by: Jenkins

diff --git a/sd/CppunitTest_sd_ui_func.mk b/sd/CppunitTest_sd_ui_func.mk
new file mode 100644
index 000000000000..bad465b194d5
--- /dev/null
+++ b/sd/CppunitTest_sd_ui_func.mk
@@ -0,0 +1,88 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitTest,sd_ui_func))
+
+$(eval $(call gb_CppunitTest_use_externals,sd_ui_func,\
+       boost_headers \
+       libxml2 \
+))
+
+$(eval $(call gb_CppunitTest_use_common_precompiled_header,sd_ui_func))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sd_ui_func, \
+    sd/qa/ui/func/func \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,sd_ui_func, \
+    basegfx \
+    canvastools \
+    comphelper \
+    cppcanvas \
+    cppu \
+    cppuhelper \
+    docmodel \
+    drawinglayer \
+    editeng \
+    for \
+    forui \
+    i18nlangtag \
+    i18nutil \
+    msfilter \
+    oox \
+    sal \
+    salhelper \
+    sax \
+    sb \
+    sd \
+    sfx \
+    sot \
+    subsequenttest \
+    svl \
+    svt \
+    svx \
+    svxcore \
+    test \
+    tl \
+    tk \
+    ucbhelper \
+    unotest \
+    utl \
+    vcl \
+    xo \
+))
+
+$(eval $(call gb_CppunitTest_set_include,sd_ui_func,\
+    -I$(SRCDIR)/sd/inc \
+    -I$(SRCDIR)/sd/source/ui/inc \
+    -I$(SRCDIR)/sd/source/ui/slidesorter/inc \
+    -I$(SRCDIR)/sd/qa/unit \
+    $$(INCLUDE) \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,sd_ui_func))
+
+$(eval $(call gb_CppunitTest_use_ure,sd_ui_func))
+$(eval $(call gb_CppunitTest_use_vcl,sd_ui_func))
+
+$(eval $(call gb_CppunitTest_use_rdb,sd_ui_func,services))
+
+$(eval $(call gb_CppunitTest_use_custom_headers,sd_ui_func,\
+       officecfg/registry \
+))
+
+$(eval $(call gb_CppunitTest_use_configuration,sd_ui_func))
+
+$(eval $(call gb_CppunitTest_add_arguments,sd_ui_func, \
+    
-env:arg-env=$(gb_Helper_LIBRARY_PATH_VAR)"$$$${$(gb_Helper_LIBRARY_PATH_VAR)+=$$$$$(gb_Helper_LIBRARY_PATH_VAR)}"
 \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sd/Module_sd.mk b/sd/Module_sd.mk
index aefdbb587dd4..048e2743a835 100644
--- a/sd/Module_sd.mk
+++ b/sd/Module_sd.mk
@@ -45,6 +45,7 @@ $(eval $(call gb_Module_add_slowcheck_targets,sd,\
     CppunitTest_sd_layout_tests \
     CppunitTest_sd_misc_tests \
     CppunitTest_sd_uiimpress \
+    CppunitTest_sd_ui_func \
     CppunitTest_sd_html_export_tests \
     CppunitTest_sd_activex_controls_tests \
     CppunitTest_sd_pdf_import_test \
diff --git a/sd/qa/ui/func/func.cxx b/sd/qa/ui/func/func.cxx
new file mode 100644
index 000000000000..f6df8fcc8857
--- /dev/null
+++ b/sd/qa/ui/func/func.cxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sdmodeltestbase.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+
+#include <comphelper/sequenceashashmap.hxx>
+#include <vcl/scheduler.hxx>
+
+#include <DrawDocShell.hxx>
+#include <ViewShell.hxx>
+#include <sdpage.hxx>
+#include <unomodel.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+/// Covers sd/source/ui/func/ fixes.
+class Test : public SdModelTestBase
+{
+public:
+    Test()
+        : SdModelTestBase(u"/sd/qa/unit/data/"_ustr)
+    {
+    }
+};
+
+CPPUNIT_TEST_FIXTURE(Test, testNoneToBullet)
+{
+    // Given a document with a shape, the only paragraph has a numbering of 
type "none":
+    createSdImpressDoc("odp/none-to-bullet.odp");
+    sd::ViewShell* pViewShell = getSdDocShell()->GetViewShell();
+    SdPage* pPage = pViewShell->GetActualPage();
+    SdrObject* pShape = pPage->GetObj(0);
+    CPPUNIT_ASSERT(pShape);
+    SdrView* pView = pViewShell->GetView();
+    pView->MarkObj(pShape, pView->GetSdrPageView());
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT(!pView->IsTextEdit());
+
+    // When turning the "none" numbering to a bullet:
+    // Start text edit:
+    auto pImpressDocument = 
dynamic_cast<SdXImpressDocument*>(mxComponent.get());
+    typeString(pImpressDocument, u"x");
+    CPPUNIT_ASSERT(pView->IsTextEdit());
+    // Do the switch:
+    dispatchCommand(mxComponent, u".uno:DefaultBullet"_ustr, {});
+    // End text edit:
+    typeKey(pImpressDocument, KEY_ESCAPE);
+
+    // Then make sure we switch to a bullet with reasonable defaults:
+    CPPUNIT_ASSERT(!pView->IsTextEdit());
+    uno::Reference<drawing::XDrawPagesSupplier> 
xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
+    uno::Reference<drawing::XDrawPage> 
xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+                                                 uno::UNO_QUERY);
+    uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), 
uno::UNO_QUERY);
+    uno::Reference<beans::XPropertySet> xParagraph(getParagraphFromShape(0, 
xShape),
+                                                   uno::UNO_QUERY);
+    // Check the list level 1 properties:
+    uno::Reference<container::XIndexAccess> xNumberingRules;
+    xParagraph->getPropertyValue(u"NumberingRules"_ustr) >>= xNumberingRules;
+    comphelper::SequenceAsHashMap 
aNumberingRule(xNumberingRules->getByIndex(0));
+    sal_Int32 nLeftMargin{};
+    aNumberingRule[u"LeftMargin"_ustr] >>= nLeftMargin;
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 1200
+    // - Actual  : 0
+    // i.e. there was no left margin at all, first and later lines did not 
match on the left hand
+    // side.
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1200), nLeftMargin);
+    sal_Int32 nFirstLineOffset{};
+    aNumberingRule[u"FirstLineOffset"_ustr] >>= nFirstLineOffset;
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-900), nFirstLineOffset);
+}
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/qa/unit/data/odp/none-to-bullet.odp 
b/sd/qa/unit/data/odp/none-to-bullet.odp
new file mode 100644
index 000000000000..3526ae3916cf
Binary files /dev/null and b/sd/qa/unit/data/odp/none-to-bullet.odp differ
diff --git a/sd/qa/unit/sdmodeltestbase.hxx b/sd/qa/unit/sdmodeltestbase.hxx
index 8a398520e6a8..135245767d03 100644
--- a/sd/qa/unit/sdmodeltestbase.hxx
+++ b/sd/qa/unit/sdmodeltestbase.hxx
@@ -34,6 +34,8 @@
 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
 #include <com/sun/star/packages/zip/ZipFileAccess.hpp>
 #include <drawinglayer/XShapeDumper.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <vcl/scheduler.hxx>
 #include <com/sun/star/text/XTextField.hpp>
 
 using namespace ::com::sun::star;
@@ -184,6 +186,23 @@ public:
 
         return pXmlDoc;
     }
+
+    void typeString(SdXImpressDocument* rImpressDocument, std::u16string_view 
rStr)
+    {
+        for (const char16_t c : rStr)
+        {
+            rImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, c, 0);
+            rImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, c, 0);
+            Scheduler::ProcessEventsToIdle();
+        }
+    }
+
+    void typeKey(SdXImpressDocument* rImpressDocument, const sal_uInt16 nKey)
+    {
+        rImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, nKey);
+        rImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, nKey);
+        Scheduler::ProcessEventsToIdle();
+    }
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/qa/unit/uiimpress.cxx b/sd/qa/unit/uiimpress.cxx
index 98b1cdb01974..215af8589928 100644
--- a/sd/qa/unit/uiimpress.cxx
+++ b/sd/qa/unit/uiimpress.cxx
@@ -80,8 +80,6 @@ public:
     }
 
     void checkCurrentPageNumber(sal_uInt16 nNum);
-    void typeString(SdXImpressDocument* rImpressDocument, std::u16string_view 
rStr);
-    void typeKey(SdXImpressDocument* rImpressDocument, const sal_uInt16 nKey);
     void insertStringToObject(sal_uInt16 nObj, std::u16string_view rStr, bool 
bUseEscape);
     sd::slidesorter::SlideSorterViewShell* getSlideSorterViewShell();
     void lcl_search(const OUString& rKey, bool bFindAll = false, bool 
bBackwards = false);
@@ -99,23 +97,6 @@ void SdUiImpressTest::checkCurrentPageNumber(sal_uInt16 nNum)
     CPPUNIT_ASSERT_EQUAL(nNum, nPageNumber);
 }
 
-void SdUiImpressTest::typeKey(SdXImpressDocument* rImpressDocument, const 
sal_uInt16 nKey)
-{
-    rImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, nKey);
-    rImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, nKey);
-    Scheduler::ProcessEventsToIdle();
-}
-
-void SdUiImpressTest::typeString(SdXImpressDocument* rImpressDocument, 
std::u16string_view rStr)
-{
-    for (const char16_t c : rStr)
-    {
-        rImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, c, 0);
-        rImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, c, 0);
-        Scheduler::ProcessEventsToIdle();
-    }
-}
-
 void SdUiImpressTest::insertStringToObject(sal_uInt16 nObj, 
std::u16string_view rStr,
                                            bool bUseEscape)
 {
diff --git a/sd/source/ui/func/fuolbull.cxx b/sd/source/ui/func/fuolbull.cxx
index ef4dd373fcbc..ecb7c5377a1b 100644
--- a/sd/source/ui/func/fuolbull.cxx
+++ b/sd/source/ui/func/fuolbull.cxx
@@ -206,7 +206,13 @@ void 
FuBulletAndPosition::SetCurrentBulletsNumbering(SfxRequest& rReq)
             {
                 if(nActNumLvl & nMask)
                 {
-                    const SvxNumberFormat& aFmt(aTmpRule.GetLevel(i));
+                    SvxNumberFormat aFmt(aTmpRule.GetLevel(i));
+                    if (nSId == FN_SVX_SET_BULLET)
+                    {
+                        // If changing to a bullet, then make its format and 
indent has a good
+                        // default, similar to what the master page offers:
+                        
SdStyleSheetPool::setDefaultOutlineNumberFormatBulletAndIndent(i, aFmt);
+                    }
                     pNumRule->SetLevel(i, aFmt);
                 }
                 nMask <<= 1;

Reply via email to