tdf#161056 Show bullets used in document in bullets dropdown

Change-Id: I40cfc39501006146f7c6c04a1f3c7cf877c6f1c4
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/167186
Tested-by: Jenkins
Reviewed-by: Samuel Mehrbrodt <[email protected]>
diff --git a/include/svx/nbdtmg.hxx b/include/svx/nbdtmg.hxx
index 894304e..8979b73 100644
--- a/include/svx/nbdtmg.hxx
+++ b/include/svx/nbdtmg.hxx
@@ -145,7 +145,7 @@ class SVX_DLLPUBLIC NBOTypeMgrBase
};


class BulletsTypeMgr final : public NBOTypeMgrBase
class SVX_DLLPUBLIC BulletsTypeMgr final : public NBOTypeMgrBase
{
    friend class OutlineTypeMgr;
    friend class NumberingTypeMgr;
@@ -161,6 +161,7 @@ class BulletsTypeMgr final : public NBOTypeMgrBase
        virtual sal_uInt16 GetNBOIndexForNumRule(SvxNumRule& aNum,sal_uInt16 mLevel,sal_uInt16 nFromIndex=0) override;
        virtual void ReplaceNumRule(SvxNumRule& aNum, sal_uInt16 nIndex, sal_uInt16 mLevel) override;
        virtual void ApplyNumRule(SvxNumRule& aNum, sal_uInt16 nIndex, sal_uInt16 mLevel, bool isDefault=false,bool isResetSize=false) override;
        void ApplyCustomRule(SvxNumRule& aNum, std::u16string_view sBullet, std::u16string_view sFont, sal_uInt16 mLevel,bool isResetSize=false);
        virtual OUString GetDescription(sal_uInt16 nIndex, bool isDefault) override;
        virtual bool IsCustomized(sal_uInt16 nIndex) override;
        static BulletsTypeMgr& GetInstance();
diff --git a/include/svx/numvset.hxx b/include/svx/numvset.hxx
index 059d6b7..d3b6e71 100644
--- a/include/svx/numvset.hxx
+++ b/include/svx/numvset.hxx
@@ -40,6 +40,7 @@ namespace com::sun::star {

enum class NumberingPageType
{
    DOCBULLET,
    BULLET,
    SINGLENUM,
    OUTLINE,
@@ -55,6 +56,9 @@ class SVX_DLLPUBLIC SvxNumValueSet : public ValueSet
    css::uno::Reference<css::text::XNumberingFormatter> xFormatter;
    css::lang::Locale aLocale;

    // Pair of bullet chars (first), and their respective font (second)
    std::vector<std::pair<OUString, OUString>> maCustomBullets;

    css::uno::Sequence<
        css::uno::Sequence<
            css::beans::PropertyValue> > aNumSettings;
@@ -82,6 +86,9 @@ public:
            css::uno::Reference<css::text::XNumberingFormatter> const & xFormatter,
            const css::lang::Locale& rLocale);

    std::vector<std::pair<OUString, OUString>> GetCustomBullets() { return maCustomBullets; }
    void SetCustomBullets(std::vector<std::pair<OUString, OUString>> aCustomBullets);

    virtual FactoryFunction GetUITestFactory() const override;

};
diff --git a/include/svx/svxids.hrc b/include/svx/svxids.hrc
index b4e1a91..68d02d4 100644
--- a/include/svx/svxids.hrc
+++ b/include/svx/svxids.hrc
@@ -185,6 +185,7 @@ class XFillGradientItem;
#define FN_BUL_NUM_RULE_INDEX   TypedWhichId<SfxUInt16Item>(FN_EDIT + 120) // achieving num rule index
#define FN_NUM_NUM_RULE_INDEX   TypedWhichId<SfxUInt16Item>(FN_EDIT + 121)
#define FN_OUTLINE_RULE_INDEX   TypedWhichId<SfxUInt16Item>(FN_EDIT + 122)
#define FN_BUL_GET_DOC_BULLETS  TypedWhichId<SfxStringListItem>(FN_EDIT + 123)

#define FN_INSERT               (SID_SW_START +  300) // 20300
#define FN_DELETE_BOOKMARK      TypedWhichId<SfxStringItem>(FN_INSERT + 1)
@@ -1058,8 +1059,12 @@ class XFillGradientItem;
#define SID_CHAR_DLG_FOR_PARAGRAPH                      ( SID_SVX_START + 1210 )
#define SID_SET_DOCUMENT_LANGUAGE                       TypedWhichId<SfxBoolItem>( SID_SVX_START + 1211 )

#define SID_ATTR_BULLET_CHAR                            TypedWhichId<SfxStringItem>(SID_SVX_START + 1212)
#define SID_ATTR_BULLET_FONT                            TypedWhichId<SfxStringItem>(SID_SVX_START + 1213)
#define SID_ATTR_BULLET_INDEX                           TypedWhichId<SfxUInt16Item>(SID_SVX_START + 1214)

// IMPORTANT NOTE: adjust SID_SVX_FIRSTFREE, when adding new slot id
#define SID_SVX_FIRSTFREE                               ( SID_SVX_START + 1211 + 1 )
#define SID_SVX_FIRSTFREE                               ( SID_SVX_START + 1214 + 1 )


// Overflow check for slot IDs
diff --git a/sd/source/ui/func/fuolbull.cxx b/sd/source/ui/func/fuolbull.cxx
index 5c518d4..076c097 100644
--- a/sd/source/ui/func/fuolbull.cxx
+++ b/sd/source/ui/func/fuolbull.cxx
@@ -123,7 +123,7 @@ void FuBulletAndPosition::SetCurrentBulletsNumbering(SfxRequest& rReq)
        return;
    }

    const SfxUInt16Item* pItem = rReq.GetArg<SfxUInt16Item>(nSId);
    const SfxUInt16Item* pItem = rReq.GetArgs()->GetItem(SID_ATTR_BULLET_INDEX);
    if ( !pItem )
    {
        rReq.Done();
diff --git a/svx/sdi/svx.sdi b/svx/sdi/svx.sdi
index d1eea8e..59bae83 100644
--- a/svx/sdi/svx.sdi
+++ b/svx/sdi/svx.sdi
@@ -4991,6 +4991,21 @@ SfxUInt16Item CurrentNumListType FN_NUM_NUM_RULE_INDEX
    GroupId = SfxGroupId::Enumeration;
]

SfxStringListItem DocumentBulletList FN_BUL_GET_DOC_BULLETS
[
    AutoUpdate = TRUE,
    FastCall = FALSE,
    ReadOnlyDoc = FALSE,
    Toggle = TRUE,
    Container = FALSE,
    RecordAbsolute = FALSE,
    RecordPerSet;

    AccelConfig = FALSE,
    MenuConfig = FALSE,
    ToolBoxConfig = FALSE,
    GroupId = SfxGroupId::Enumeration;
]

SfxVoidItem InsertObject SID_INSERT_OBJECT
( SfxGlobalNameItem ClassId SID_INSERT_OBJECT )
@@ -6137,6 +6152,9 @@ SfxUInt16Item SetNumber FN_SVX_SET_NUMBER


SfxUInt16Item SetBullet FN_SVX_SET_BULLET
(SfxStringItem BulletChar SID_ATTR_BULLET_CHAR,
 SfxStringItem BulletFont SID_ATTR_BULLET_FONT,
 SfxUInt16Item BulletIndex SID_ATTR_BULLET_INDEX)
[
    AutoUpdate = TRUE,
    FastCall = FALSE,
diff --git a/svx/source/dialog/svxbmpnumvalueset.cxx b/svx/source/dialog/svxbmpnumvalueset.cxx
index 4422560..a934a97 100644
--- a/svx/source/dialog/svxbmpnumvalueset.cxx
+++ b/svx/source/dialog/svxbmpnumvalueset.cxx
@@ -149,13 +149,18 @@ void SvxNumValueSet::UserDraw( const UserDrawEvent& rUDEvt )
    aRuleFont.SetFillColor(aBackColor);
    css::uno::Sequence< OUString > aBulletSymbols;

    if(ePageType == NumberingPageType::BULLET)
    if(ePageType == NumberingPageType::BULLET || ePageType == NumberingPageType::DOCBULLET)
    {
        aBulletSymbols = officecfg::Office::Common::BulletsNumbering::DefaultBullets::get();
        css::uno::Sequence< OUString > aBulletFonts(officecfg::Office::Common::BulletsNumbering::DefaultBulletsFonts::get());
        aRuleFont.SetFamilyName(aBulletFonts[nIndex]);
        aFont = aRuleFont;
    }
    else if (ePageType == NumberingPageType::DOCBULLET)
    {
        aRuleFont.SetFamilyName(maCustomBullets[nIndex].second);
        aFont = aRuleFont;
    }
    else if(ePageType == NumberingPageType::OUTLINE)
    {
        aSize.setHeight( nRectHeight/8 );
@@ -198,7 +203,8 @@ void SvxNumValueSet::UserDraw( const UserDrawEvent& rUDEvt )
    // Now comes the text
    static constexpr OUStringLiteral sValue(u"Value");
    if( NumberingPageType::SINGLENUM == ePageType ||
           NumberingPageType::BULLET == ePageType )
           NumberingPageType::BULLET == ePageType ||
           NumberingPageType::DOCBULLET == ePageType)
    {
        Point aStart(aBLPos.X() + nRectWidth / 9,0);
        for( sal_uInt16 i = 0; i < 3; i++ )
@@ -212,6 +218,12 @@ void SvxNumValueSet::UserDraw( const UserDrawEvent& rUDEvt )
                aStart.AdjustY( -(pDev->GetTextHeight()/2) );
                aStart.setX( aBLPos.X() + 5 );
            }
            else if (ePageType == NumberingPageType::DOCBULLET)
            {
                sText = maCustomBullets[nIndex].first;
                aStart.AdjustY( -(pDev->GetTextHeight()/2) );
                aStart.setX( aBLPos.X() + 5 );
            }
            else
            {
                if(xFormatter.is() && aNumSettings.getLength() > nIndex)
@@ -446,6 +458,16 @@ void SvxNumValueSet::SetOutlineNumberingSettings(
    }
}

void SvxNumValueSet::SetCustomBullets(std::vector<std::pair<OUString, OUString>> aCustomBullets)
{
    Clear();
    maCustomBullets = aCustomBullets;
    for (size_t i = 0; i < aCustomBullets.size(); i++)
    {
        InsertItem(i + 1, i);
    }
}

SvxBmpNumValueSet::SvxBmpNumValueSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow)
    : SvxNumValueSet(std::move(pScrolledWindow))
    , aFormatIdle("SvxBmpNumValueSet FormatIdle")
diff --git a/svx/source/sidebar/nbdtmg.cxx b/svx/source/sidebar/nbdtmg.cxx
index ed88add..14a27fe 100644
--- a/svx/source/sidebar/nbdtmg.cxx
+++ b/svx/source/sidebar/nbdtmg.cxx
@@ -341,6 +341,30 @@ void BulletsTypeMgr::ApplyNumRule(SvxNumRule& aNum, sal_uInt16 nIndex, sal_uInt1
    }
}

void BulletsTypeMgr::ApplyCustomRule(SvxNumRule& aNum, std::u16string_view sBullet,
                                     std::u16string_view sFont, sal_uInt16 mLevel, bool isResetSize)
{
    sal_uInt16 nMask = 1;
    OUString sBulletCharFormatName = GetBulletCharFmtName();
    const vcl::Font aFont(OUString(sFont), Size(1, 1));
    for (sal_uInt16 i = 0; i < aNum.GetLevelCount(); i++)
    {
        if (mLevel & nMask)
        {
            SvxNumberFormat aFmt(aNum.GetLevel(i));
            aFmt.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
            aFmt.SetBulletFont(&aFont);
            aFmt.SetBulletChar(sBullet[0]);
            aFmt.SetCharFormatName(sBulletCharFormatName);
            aFmt.SetListFormat("");
            if (isResetSize)
                aFmt.SetBulletRelSize(45);
            aNum.SetLevel(i, aFmt);
        }
        nMask <<= 1;
    }
}

OUString BulletsTypeMgr::GetDescription(sal_uInt16 /*nIndex*/, bool /*isDefault*/)
{
    return OUString();
diff --git a/svx/source/tbxctrls/bulletsnumbering.cxx b/svx/source/tbxctrls/bulletsnumbering.cxx
index f6ca64b..0ce9057 100644
--- a/svx/source/tbxctrls/bulletsnumbering.cxx
+++ b/svx/source/tbxctrls/bulletsnumbering.cxx
@@ -9,9 +9,11 @@

#include <com/sun/star/text/DefaultNumberingProvider.hpp>
#include <com/sun/star/text/XNumberingFormatter.hpp>
#include <com/sun/star/uno/Sequence.hxx>

#include <comphelper/propertysequence.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <officecfg/Office/Common.hxx>
#include <svtools/popupwindowcontroller.hxx>
#include <svtools/toolbarmenu.hxx>
#include <svx/strings.hrc>
@@ -32,8 +34,13 @@ class NumberingPopup : public WeldToolbarPopup
    NumberingToolBoxControl& mrController;
    std::unique_ptr<SvxNumValueSet> mxValueSet;
    std::unique_ptr<weld::CustomWeld> mxValueSetWin;
    std::unique_ptr<SvxNumValueSet> mxValueSetDoc;
    std::unique_ptr<weld::CustomWeld> mxValueSetWinDoc;
    std::unique_ptr<weld::Button> mxMoreButton;
    std::unique_ptr<weld::Label> mxBulletsLabel;
    std::unique_ptr<weld::Label> mxDocBulletsLabel;
    DECL_LINK(VSSelectValueSetHdl, ValueSet*, void);
    DECL_LINK(VSSelectValueSetDocHdl, ValueSet*, void);
    DECL_LINK(VSButtonClickSetHdl, weld::Button&, void);

    virtual void GrabFocus() override;
@@ -70,13 +77,22 @@ NumberingPopup::NumberingPopup(NumberingToolBoxControl& rController,
    , mrController(rController)
    , mxValueSet(new SvxNumValueSet(m_xBuilder->weld_scrolled_window(u"valuesetwin"_ustr, true)))
    , mxValueSetWin(new weld::CustomWeld(*m_xBuilder, u"valueset"_ustr, *mxValueSet))
    , mxValueSetDoc(new SvxNumValueSet(m_xBuilder->weld_scrolled_window(u"valuesetwin_doc"_ustr, true)))
    , mxValueSetWinDoc(new weld::CustomWeld(*m_xBuilder, u"valueset_doc"_ustr, *mxValueSetDoc))
    , mxMoreButton(m_xBuilder->weld_button(u"more"_ustr))
    , mxBulletsLabel(m_xBuilder->weld_label(u"label_default"_ustr))
    , mxDocBulletsLabel(m_xBuilder->weld_label(u"label_doc"_ustr))
{
    mxValueSet->SetStyle(WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NO_DIRECTSELECT);
    mxValueSetDoc->SetStyle(WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NO_DIRECTSELECT);
    mxValueSet->init(mePageType);
    mxValueSetDoc->init(NumberingPageType::DOCBULLET);
    mxValueSetWinDoc->hide();
    mxDocBulletsLabel->hide();

    if ( mePageType != NumberingPageType::BULLET )
    {
        mxBulletsLabel->hide();
        css::uno::Reference< css::text::XDefaultNumberingProvider > xDefNum = css::text::DefaultNumberingProvider::create( mrController.getContext() );
        if ( xDefNum.is() )
        {
@@ -99,16 +115,24 @@ NumberingPopup::NumberingPopup(NumberingToolBoxControl& rController,
    }

    weld::DrawingArea* pDrawingArea = mxValueSet->GetDrawingArea();
    weld::DrawingArea* pDrawingAreaDoc = mxValueSetDoc->GetDrawingArea();
    OutputDevice& rRefDevice = pDrawingArea->get_ref_device();
    Size aItemSize(rRefDevice.LogicToPixel(Size(30, 42), MapMode(MapUnit::MapAppFont)));
    mxValueSet->SetExtraSpacing( 2 );
    mxValueSetDoc->SetExtraSpacing( 2 );
    Size aSize(mxValueSet->CalcWindowSizePixel(aItemSize));
    pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
    pDrawingAreaDoc->set_size_request(aSize.Width(), aSize.Height());
    mxValueSet->SetOutputSizePixel(aSize);
    mxValueSetDoc->SetOutputSizePixel(aSize);
    mxValueSet->SetColor(Application::GetSettings().GetStyleSettings().GetFieldColor());
    mxValueSetDoc->SetColor(Application::GetSettings().GetStyleSettings().GetFieldColor());

    if ( mePageType == NumberingPageType::BULLET )
    {
        AddStatusListener( u".uno:CurrentBulletListType"_ustr );
        AddStatusListener( u".uno:DocumentBulletList"_ustr );
    }
    else if ( mePageType == NumberingPageType::SINGLENUM )
        AddStatusListener( u".uno:CurrentNumListType"_ustr );
    else
@@ -119,15 +143,67 @@ NumberingPopup::NumberingPopup(NumberingToolBoxControl& rController,
    mxMoreButton->connect_clicked(LINK(this, NumberingPopup, VSButtonClickSetHdl));

    mxValueSet->SetSelectHdl(LINK(this, NumberingPopup, VSSelectValueSetHdl));
    mxValueSetDoc->SetSelectHdl(LINK(this, NumberingPopup, VSSelectValueSetDocHdl));
}

namespace
{
bool lcl_BulletIsDefault(std::u16string_view aSymbol, std::u16string_view aFont)
{
    css::uno::Sequence<OUString> aBulletSymbols
        = officecfg::Office::Common::BulletsNumbering::DefaultBullets::get();
    css::uno::Sequence<OUString> aBulletFonts
        = officecfg::Office::Common::BulletsNumbering::DefaultBulletsFonts::get();
    for (sal_Int32 i = 0; i < aBulletSymbols.getLength(); i++)
    {
        if (aBulletSymbols[i] == aSymbol && aBulletFonts[i] == aFont)
            return true;
    }
    return false;
}
}

void NumberingPopup::statusChanged( const css::frame::FeatureStateEvent& rEvent )
{
    mxValueSet->SetNoSelection();

    sal_Int32 nSelItem;
    if ( rEvent.State >>= nSelItem )
        mxValueSet->SelectItem( nSelItem );
    if (rEvent.FeatureURL.Complete == ".uno:DocumentBulletList")
    {
        css::uno::Sequence<OUString> aSeq;
        if (rEvent.State >>= aSeq)
        {
            std::vector<std::pair<OUString, OUString>> aList;
            mxValueSetDoc->Clear();
            int i = 1;
            // The string contains the bullet as first character, and then the font name
            for (const OUString& sBulletFont : aSeq)
            {
                OUString sBullet(sBulletFont.copy(0, 1));
                OUString sFont(sBulletFont.copy(1, sBulletFont.getLength() - 1));
                if (lcl_BulletIsDefault(sBullet, sFont))
                    continue;
                mxValueSetDoc->InsertItem(i, sBullet, i);
                aList.emplace_back(sBullet, sFont);
                i++;
            }
            if (!aList.empty())
            {
                mxValueSetWinDoc->show();
                mxDocBulletsLabel->show();
                mxValueSetDoc->SetCustomBullets(aList);
            }
            else
            {
                mxValueSetWinDoc->hide();
                mxDocBulletsLabel->hide();
            }
        }
    }
    else
    {
        mxValueSet->SetNoSelection();
        sal_Int32 nSelItem;
        if ( rEvent.State >>= nSelItem )
            mxValueSet->SelectItem( nSelItem );
    }
}

IMPL_LINK_NOARG(NumberingPopup, VSSelectValueSetHdl, ValueSet*, void)
@@ -135,7 +211,7 @@ IMPL_LINK_NOARG(NumberingPopup, VSSelectValueSetHdl, ValueSet*, void)
    sal_uInt16 nSelItem = mxValueSet->GetSelectedItemId();
    if ( mePageType == NumberingPageType::BULLET )
    {
        auto aArgs( comphelper::InitPropertySequence( { { "SetBullet", css::uno::Any( nSelItem ) } } ) );
        auto aArgs( comphelper::InitPropertySequence( { { "BulletIndex", css::uno::Any( nSelItem ) } } ) );
        mrController.dispatchCommand( u".uno:SetBullet"_ustr, aArgs );
    }
    else if ( mePageType == NumberingPageType::SINGLENUM )
@@ -151,6 +227,18 @@ IMPL_LINK_NOARG(NumberingPopup, VSSelectValueSetHdl, ValueSet*, void)
    mrController.EndPopupMode();
}

IMPL_LINK_NOARG(NumberingPopup, VSSelectValueSetDocHdl, ValueSet*, void)
{
    sal_uInt16 nSelItem = mxValueSetDoc->GetSelectedItemId() - 1;
    auto aCustomBullets = mxValueSetDoc->GetCustomBullets();
    OUString nChar(aCustomBullets[nSelItem].first);
    OUString sFont(aCustomBullets[nSelItem].second);
    auto aArgs(comphelper::InitPropertySequence(
        { { "BulletChar", css::uno::Any(nChar) }, { "BulletFont", css::uno::Any(sFont) } }));
    mrController.dispatchCommand(".uno:SetBullet", aArgs);
    mrController.EndPopupMode();
}

void NumberingPopup::GrabFocus()
{
    mxValueSet->GrabFocus();
diff --git a/svx/uiconfig/ui/numberingwindow.ui b/svx/uiconfig/ui/numberingwindow.ui
index db44aee..c92acae 100644
--- a/svx/uiconfig/ui/numberingwindow.ui
+++ b/svx/uiconfig/ui/numberingwindow.ui
@@ -19,6 +19,21 @@
        <property name="orientation">vertical</property>
        <property name="spacing">6</property>
        <child>
          <object class="GtkLabel" id="label_default">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="label" translatable="yes" context="numberingwindow|label_default">Bullet Library</property>
            <accessibility>
              <relation type="label-for" target="valueset"/>
            </accessibility>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkScrolledWindow" id="valuesetwin">
            <property name="visible">True</property>
            <property name="can-focus">True</property>
@@ -38,6 +53,9 @@
                    <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
                    <property name="hexpand">True</property>
                    <property name="vexpand">True</property>
                    <accessibility>
                      <relation type="labelled-by" target="label_default"/>
                    </accessibility>
                  </object>
                </child>
              </object>
@@ -50,6 +68,55 @@
          </packing>
        </child>
        <child>
          <object class="GtkLabel" id="label_doc">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="label" translatable="yes" context="numberingwindow|label_doc">Document Bullets</property>
            <accessibility>
              <relation type="label-for" target="valueset_doc"/>
            </accessibility>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkScrolledWindow" id="valuesetwin_doc">
            <property name="visible">True</property>
            <property name="can-focus">True</property>
            <property name="hexpand">True</property>
            <property name="vexpand">True</property>
            <property name="hscrollbar-policy">never</property>
            <property name="vscrollbar-policy">never</property>
            <property name="shadow-type">in</property>
            <child>
              <object class="GtkViewport">
                <property name="visible">True</property>
                <property name="can-focus">False</property>
                <child>
                  <object class="GtkDrawingArea" id="valueset_doc">
                    <property name="visible">True</property>
                    <property name="can-focus">False</property>
                    <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
                    <property name="hexpand">True</property>
                    <property name="vexpand">True</property>
                    <accessibility>
                      <relation type="labelled-by" target="label_doc"/>
                    </accessibility>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">3</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="more">
            <property name="label" translatable="yes" context="numberingwindow|more">Customize...</property>
            <property name="visible">True</property>
@@ -62,7 +129,7 @@
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
            <property name="position">4</property>
          </packing>
        </child>
      </object>
diff --git a/sw/inc/doc.hxx b/sw/inc/doc.hxx
index 5dc6253..b4d3d0e 100644
--- a/sw/inc/doc.hxx
+++ b/sw/inc/doc.hxx
@@ -1095,6 +1095,7 @@ public:
        const SvxNumberFormat::SvxNumPositionAndSpaceMode eDefaultNumberFormatPositionAndSpaceMode =
            SvxNumberFormat::LABEL_WIDTH_AND_POSITION );
    sal_uInt16 FindNumRule( std::u16string_view rName ) const;
    std::vector<OUString> GetUsedBullets();
    SW_DLLPUBLIC SwNumRule* FindNumRulePtr( const OUString& rName ) const;

    // Deletion only possible if Rule is not used!
diff --git a/sw/inc/editsh.hxx b/sw/inc/editsh.hxx
index a4dcecd..85eb84b 100644
--- a/sw/inc/editsh.hxx
+++ b/sw/inc/editsh.hxx
@@ -578,6 +578,7 @@ public:
     text node belongs, which applies the found list style. */
    const SwNumRule * SearchNumRule(const bool bNum,
                                    OUString& sListId );
    std::vector<OUString> GetUsedBullets();

    /** Undo.
     Maintain UndoHistory in Document.
diff --git a/sw/sdi/_textsh.sdi b/sw/sdi/_textsh.sdi
index e5a4bfd..4c5a48b 100644
--- a/sw/sdi/_textsh.sdi
+++ b/sw/sdi/_textsh.sdi
@@ -810,6 +810,11 @@ interface BaseText
        StateMethod = GetState ;
        DisableFlags="SfxDisableFlags::SwOnProtectedCursor";
    ]
    FN_BUL_GET_DOC_BULLETS // status(final|play)
    [
        StateMethod = GetState ;
        DisableFlags="SfxDisableFlags::SwOnProtectedCursor";
    ]
    FN_NUMBER_BULLETS // status(final|play)
    [
        ExecMethod = ExecEnterNum ;
diff --git a/sw/source/core/doc/docnum.cxx b/sw/source/core/doc/docnum.cxx
index 5378a96..301fe70 100644
--- a/sw/source/core/doc/docnum.cxx
+++ b/sw/source/core/doc/docnum.cxx
@@ -2458,6 +2458,26 @@ sal_uInt16 SwDoc::FindNumRule( std::u16string_view rName ) const
    return USHRT_MAX;
}

std::vector<OUString> SwDoc::GetUsedBullets()
{
    std::vector<OUString> aUsedBullets;
    for (size_t nRule = 0; nRule < mpNumRuleTable->size(); ++nRule)
    {
        for (int nLevel=0; nLevel<10; ++nLevel)
        {
            const SwNumFormat& rFormat = (*mpNumRuleTable)[nRule]->Get(nLevel);
            if (SVX_NUM_CHAR_SPECIAL != rFormat.GetNumberingType())
                continue;
            vcl::Font aFont(*rFormat.GetBulletFont());
            sal_UCS4 cBullet = rFormat.GetBulletChar();
            OUString sBullet(&cBullet, 1);
            OUString sFontName(aFont.GetFamilyName());
            aUsedBullets.emplace_back(sBullet + sFontName);
        }
    }
    return aUsedBullets;
}

SwNumRule* SwDoc::FindNumRulePtr( const OUString& rName ) const
{
    SwNumRule * pResult = maNumRuleMap[rName];
diff --git a/sw/source/core/edit/ednumber.cxx b/sw/source/core/edit/ednumber.cxx
index 372e7b1e..ad585e9 100644
--- a/sw/source/core/edit/ednumber.cxx
+++ b/sw/source/core/edit/ednumber.cxx
@@ -874,4 +874,9 @@ const SwNumRule * SwEditShell::SearchNumRule( const bool bNum,
                                    sListId, GetLayout() );
}

std::vector<OUString> SwEditShell::GetUsedBullets()
{
    return GetDoc()->GetUsedBullets();
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/shells/textsh1.cxx b/sw/source/uibase/shells/textsh1.cxx
index 39f655d..fa4de6b 100644
--- a/sw/source/uibase/shells/textsh1.cxx
+++ b/sw/source/uibase/shells/textsh1.cxx
@@ -2853,6 +2853,16 @@ void SwTextShell::GetState( SfxItemSet &rSet )
            }
        }
            break;
            case FN_BUL_GET_DOC_BULLETS:
            {
                std::vector<OUString> aBullets = rSh.GetUsedBullets();
                SfxStringListItem aItem(FN_BUL_GET_DOC_BULLETS);
                uno::Sequence<OUString> aSeq(aBullets.data(),
                                             static_cast<sal_Int32>(aBullets.size()));
                aItem.SetStringList(aSeq);
                rSet.Put(aItem);
            }
            break;
            case FN_NUM_CONTINUE:
            {
                // #i86492#
diff --git a/sw/source/uibase/shells/txtnum.cxx b/sw/source/uibase/shells/txtnum.cxx
index bdad787..1ef46f0 100644
--- a/sw/source/uibase/shells/txtnum.cxx
+++ b/sw/source/uibase/shells/txtnum.cxx
@@ -260,10 +260,12 @@ void SwTextShell::ExecSetNumber(SfxRequest const &rReq)
    case FN_SVX_SET_BULLET:
    case FN_SVX_SET_OUTLINE:
        {
            const SfxUInt16Item* pItem = rReq.GetArg<SfxUInt16Item>(nSlot);
            if ( pItem != nullptr )
            const SfxUInt16Item* pIndexItem = rReq.GetArgs()->GetItem( SID_ATTR_BULLET_INDEX );
            const SfxStringItem* pCharItem = rReq.GetArgs()->GetItem( SID_ATTR_BULLET_CHAR );
            const SfxStringItem* pFontItem = rReq.GetArgs()->GetItem( SID_ATTR_BULLET_FONT );

            if ( pIndexItem != nullptr || ( pCharItem != nullptr && pFontItem != nullptr ) )
            {
                const sal_uInt16 nChosenItemIdx = pItem->GetValue();
                svx::sidebar::NBOType nNBOType = svx::sidebar::NBOType::Bullets;
                if ( nSlot == FN_SVX_SET_NUMBER )
                    nNBOType = svx::sidebar::NBOType::Numbering;
@@ -301,7 +303,15 @@ void SwTextShell::ExecSetNumber(SfxRequest const &rReq)
                    aSet.Put( SvxNumBulletItem( aNewSvxNumRule, SID_ATTR_NUMBERING_RULE ) );

                    pNBOTypeMgr->SetItems( &aSet );
                    pNBOTypeMgr->ApplyNumRule( aNewSvxNumRule, nChosenItemIdx - 1, nActNumLvl );
                    if (pIndexItem)
                        pNBOTypeMgr->ApplyNumRule( aNewSvxNumRule, pIndexItem->GetValue() - 1, nActNumLvl );
                    else
                    {
                        svx::sidebar::BulletsTypeMgr* pBulletsTypeMgr
                            = dynamic_cast<svx::sidebar::BulletsTypeMgr*>(pNBOTypeMgr);
                        pBulletsTypeMgr->ApplyCustomRule(aNewSvxNumRule, pCharItem->GetValue(),
                                                         pFontItem->GetValue(), nActNumLvl);
                    }

                    aNewNumRule.SetSvxRule( aNewSvxNumRule, GetShell().GetDoc() );
                    aNewNumRule.SetAutoRule( true );