Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/tbxctrls/bulletsnumbering.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 */
9
10
#include <com/sun/star/text/DefaultNumberingProvider.hpp>
11
#include <com/sun/star/text/XNumberingFormatter.hpp>
12
#include <com/sun/star/uno/Sequence.hxx>
13
14
#include <comphelper/propertysequence.hxx>
15
#include <i18nlangtag/languagetag.hxx>
16
#include <officecfg/Office/Common.hxx>
17
#include <svtools/popupwindowcontroller.hxx>
18
#include <svtools/toolbarmenu.hxx>
19
#include <svx/numvset.hxx>
20
#include <tools/mapunit.hxx>
21
#include <vcl/commandinfoprovider.hxx>
22
#include <vcl/toolbox.hxx>
23
#include <vcl/settings.hxx>
24
#include <vcl/svapp.hxx>
25
#include <vcl/weld/Builder.hxx>
26
#include <vcl/weld/Button.hxx>
27
#include <vcl/weld/Label.hxx>
28
#include <vcl/weld/ScrolledWindow.hxx>
29
#include <vcl/weld/Window.hxx>
30
#include <vcl/weld/Toolbar.hxx>
31
32
namespace {
33
34
class NumberingToolBoxControl;
35
36
class NumberingPopup : public WeldToolbarPopup
37
{
38
    NumberingPageType mePageType;
39
    NumberingToolBoxControl& mrController;
40
    std::unique_ptr<SvxNumValueSet> mxValueSet;
41
    std::unique_ptr<weld::CustomWeld> mxValueSetWin;
42
    std::unique_ptr<SvxNumValueSet> mxValueSetDoc;
43
    std::unique_ptr<weld::CustomWeld> mxValueSetWinDoc;
44
    std::unique_ptr<weld::Button> mxMoreButton;
45
    std::unique_ptr<weld::Label> mxBulletsLabel;
46
    std::unique_ptr<weld::Label> mxDocBulletsLabel;
47
    DECL_LINK(VSSelectValueSetHdl, ValueSet*, void);
48
    DECL_LINK(VSSelectValueSetDocHdl, ValueSet*, void);
49
    DECL_LINK(VSButtonClickSetHdl, weld::Button&, void);
50
51
    virtual void GrabFocus() override;
52
53
public:
54
    NumberingPopup(NumberingToolBoxControl& rController, weld::Widget* pParent, NumberingPageType ePageType);
55
56
    virtual void statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
57
};
58
59
class NumberingToolBoxControl : public svt::PopupWindowController
60
{
61
    NumberingPageType mePageType;
62
63
public:
64
    explicit NumberingToolBoxControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
65
    virtual VclPtr<vcl::Window> createVclPopupWindow( vcl::Window* pParent ) override;
66
    std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override;
67
68
    // XInitialization
69
    virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& rArguments ) override;
70
71
    // XServiceInfo
72
    virtual OUString SAL_CALL getImplementationName() override;
73
    virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
74
};
75
76
}
77
78
NumberingPopup::NumberingPopup(NumberingToolBoxControl& rController,
79
                               weld::Widget* pParent, NumberingPageType ePageType)
80
0
    : WeldToolbarPopup(rController.getFrameInterface(), pParent, u"svx/ui/numberingwindow.ui"_ustr, u"NumberingWindow"_ustr)
81
0
    , mePageType(ePageType)
82
0
    , mrController(rController)
83
0
    , mxValueSet(new SvxNumValueSet(m_xBuilder->weld_scrolled_window(u"valuesetwin"_ustr, true)))
84
0
    , mxValueSetWin(new weld::CustomWeld(*m_xBuilder, u"valueset"_ustr, *mxValueSet))
85
0
    , mxValueSetDoc(new SvxNumValueSet(m_xBuilder->weld_scrolled_window(u"valuesetwin_doc"_ustr, true)))
86
0
    , mxValueSetWinDoc(new weld::CustomWeld(*m_xBuilder, u"valueset_doc"_ustr, *mxValueSetDoc))
87
0
    , mxMoreButton(m_xBuilder->weld_button(u"more"_ustr))
88
0
    , mxBulletsLabel(m_xBuilder->weld_label(u"label_default"_ustr))
89
0
    , mxDocBulletsLabel(m_xBuilder->weld_label(u"label_doc"_ustr))
90
0
{
91
0
    mxValueSet->SetStyle(WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NO_DIRECTSELECT);
92
0
    mxValueSetDoc->SetStyle(WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NO_DIRECTSELECT);
93
0
    mxValueSet->init(mePageType);
94
0
    mxValueSetDoc->init(NumberingPageType::DOCBULLET);
95
0
    mxValueSetDoc->Hide();
96
0
    mxDocBulletsLabel->hide();
97
98
0
    if ( mePageType != NumberingPageType::BULLET )
99
0
    {
100
0
        mxBulletsLabel->hide();
101
0
        css::uno::Reference< css::text::XDefaultNumberingProvider > xDefNum = css::text::DefaultNumberingProvider::create( mrController.getContext() );
102
0
        if ( xDefNum.is() )
103
0
        {
104
0
            css::lang::Locale aLocale = Application::GetSettings().GetLanguageTag().getLocale();
105
0
            css::uno::Reference< css::text::XNumberingFormatter > xFormat( xDefNum, css::uno::UNO_QUERY );
106
107
0
            if ( mePageType == NumberingPageType::SINGLENUM )
108
0
            {
109
0
                css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > aNumberings(
110
0
                    xDefNum->getDefaultContinuousNumberingLevels( aLocale ) );
111
0
                mxValueSet->SetNumberingSettings( aNumberings, xFormat, aLocale );
112
0
            }
113
0
            else if ( mePageType == NumberingPageType::OUTLINE )
114
0
            {
115
0
                css::uno::Sequence< css::uno::Reference< css::container::XIndexAccess > > aOutline(
116
0
                    xDefNum->getDefaultOutlineNumberings( aLocale ) );
117
0
                mxValueSet->SetOutlineNumberingSettings( aOutline, xFormat, aLocale );
118
0
            }
119
0
        }
120
0
    }
121
122
0
    weld::DrawingArea* pDrawingArea = mxValueSet->GetDrawingArea();
123
0
    weld::DrawingArea* pDrawingAreaDoc = mxValueSetDoc->GetDrawingArea();
124
0
    OutputDevice& rRefDevice = pDrawingArea->get_ref_device();
125
0
    Size aItemSize(rRefDevice.LogicToPixel(Size(30, 42), MapMode(MapUnit::MapAppFont)));
126
0
    mxValueSet->SetExtraSpacing( 2 );
127
0
    mxValueSetDoc->SetExtraSpacing( 2 );
128
0
    Size aSize(mxValueSet->CalcWindowSizePixel(aItemSize));
129
0
    pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
130
0
    pDrawingAreaDoc->set_size_request(aSize.Width(), aSize.Height());
131
0
    mxValueSet->SetOutputSizePixel(aSize);
132
0
    mxValueSetDoc->SetOutputSizePixel(aSize);
133
0
    mxValueSet->SetColor(Application::GetSettings().GetStyleSettings().GetFieldColor());
134
0
    mxValueSetDoc->SetColor(Application::GetSettings().GetStyleSettings().GetFieldColor());
135
136
0
    if ( mePageType == NumberingPageType::BULLET )
137
0
    {
138
0
        AddStatusListener( u".uno:CurrentBulletListType"_ustr );
139
0
        AddStatusListener( u".uno:DocumentBulletList"_ustr );
140
0
    }
141
0
    else if ( mePageType == NumberingPageType::SINGLENUM )
142
0
        AddStatusListener( u".uno:CurrentNumListType"_ustr );
143
0
    else
144
0
        AddStatusListener( u".uno:CurrentOutlineType"_ustr );
145
146
0
    auto xImage = vcl::CommandInfoProvider::GetXGraphicForCommand(u".uno:OutlineBullet"_ustr, mrController.getFrameInterface());
147
0
    mxMoreButton->set_image(xImage);
148
0
    mxMoreButton->connect_clicked(LINK(this, NumberingPopup, VSButtonClickSetHdl));
149
150
0
    mxValueSet->SetSelectHdl(LINK(this, NumberingPopup, VSSelectValueSetHdl));
151
0
    mxValueSetDoc->SetSelectHdl(LINK(this, NumberingPopup, VSSelectValueSetDocHdl));
152
0
}
153
154
namespace
155
{
156
bool lcl_BulletIsDefault(std::u16string_view aSymbol, std::u16string_view aFont)
157
0
{
158
0
    css::uno::Sequence<OUString> aBulletSymbols
159
0
        = officecfg::Office::Common::BulletsNumbering::DefaultBullets::get();
160
0
    css::uno::Sequence<OUString> aBulletFonts
161
0
        = officecfg::Office::Common::BulletsNumbering::DefaultBulletsFonts::get();
162
0
    for (sal_Int32 i = 0; i < aBulletSymbols.getLength(); i++)
163
0
    {
164
0
        if (aBulletSymbols[i] == aSymbol && aBulletFonts[i] == aFont)
165
0
            return true;
166
0
    }
167
0
    return false;
168
0
}
169
}
170
171
void NumberingPopup::statusChanged( const css::frame::FeatureStateEvent& rEvent )
172
0
{
173
0
    if (rEvent.FeatureURL.Complete == ".uno:DocumentBulletList")
174
0
    {
175
0
        css::uno::Sequence<OUString> aSeq;
176
0
        if (rEvent.State >>= aSeq)
177
0
        {
178
0
            std::vector<std::pair<OUString, OUString>> aList;
179
0
            mxValueSetDoc->Clear();
180
0
            int i = 1;
181
            // The string contains the bullet as first character, and then the font name
182
0
            for (const OUString& sBulletFont : aSeq)
183
0
            {
184
0
                OUString sBullet(sBulletFont.copy(0, 1));
185
0
                OUString sFont(sBulletFont.copy(1, sBulletFont.getLength() - 1));
186
0
                if (lcl_BulletIsDefault(sBullet, sFont))
187
0
                    continue;
188
0
                mxValueSetDoc->InsertItem(i, sBullet, i);
189
0
                aList.emplace_back(sBullet, sFont);
190
0
                i++;
191
0
            }
192
0
            if (!aList.empty())
193
0
            {
194
0
                mxValueSetDoc->Show();
195
0
                mxDocBulletsLabel->show();
196
0
                mxValueSetDoc->SetCustomBullets(aList);
197
0
            }
198
0
            else
199
0
            {
200
0
                mxValueSetDoc->Hide();
201
0
                mxDocBulletsLabel->hide();
202
0
            }
203
0
        }
204
0
    }
205
0
    else
206
0
    {
207
0
        mxValueSet->SetNoSelection();
208
0
        sal_Int32 nSelItem;
209
0
        if ( rEvent.State >>= nSelItem )
210
0
            mxValueSet->SelectItem( nSelItem );
211
0
    }
212
0
}
213
214
IMPL_LINK_NOARG(NumberingPopup, VSSelectValueSetHdl, ValueSet*, void)
215
0
{
216
0
    sal_uInt16 nSelItem = mxValueSet->GetSelectedItemId();
217
0
    if ( mePageType == NumberingPageType::BULLET )
218
0
    {
219
0
        auto aArgs( comphelper::InitPropertySequence( { { "BulletIndex", css::uno::Any( nSelItem ) } } ) );
220
0
        mrController.dispatchCommand( u".uno:SetBullet"_ustr, aArgs );
221
0
    }
222
0
    else if ( mePageType == NumberingPageType::SINGLENUM )
223
0
    {
224
0
        auto aArgs( comphelper::InitPropertySequence( { { "SetNumber", css::uno::Any( nSelItem ) } } ) );
225
0
        mrController.dispatchCommand( u".uno:SetNumber"_ustr, aArgs );
226
0
    }
227
0
    else
228
0
    {
229
0
        auto aArgs( comphelper::InitPropertySequence( { { "SetOutline", css::uno::Any( nSelItem ) } } ) );
230
0
        mrController.dispatchCommand( u".uno:SetOutline"_ustr, aArgs );
231
0
    }
232
0
    mrController.EndPopupMode();
233
0
}
234
235
IMPL_LINK_NOARG(NumberingPopup, VSSelectValueSetDocHdl, ValueSet*, void)
236
0
{
237
0
    sal_uInt16 nSelItem = mxValueSetDoc->GetSelectedItemId() - 1;
238
0
    auto aCustomBullets = mxValueSetDoc->GetCustomBullets();
239
0
    OUString nChar(aCustomBullets[nSelItem].first);
240
0
    OUString sFont(aCustomBullets[nSelItem].second);
241
0
    auto aArgs(comphelper::InitPropertySequence(
242
0
        { { "BulletChar", css::uno::Any(nChar) }, { "BulletFont", css::uno::Any(sFont) } }));
243
0
    mrController.dispatchCommand(u".uno:SetBullet"_ustr, aArgs);
244
0
    mrController.EndPopupMode();
245
0
}
246
247
void NumberingPopup::GrabFocus()
248
0
{
249
0
    mxValueSet->GrabFocus();
250
0
}
251
252
IMPL_LINK_NOARG(NumberingPopup, VSButtonClickSetHdl, weld::Button&, void)
253
0
{
254
0
    auto aArgs( comphelper::InitPropertySequence( { { "Page", css::uno::Any( u"customize"_ustr ) } } ) );
255
0
    mrController.dispatchCommand( u".uno:OutlineBullet"_ustr, aArgs );
256
257
0
    mrController.EndPopupMode();
258
0
}
259
260
NumberingToolBoxControl::NumberingToolBoxControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext ):
261
0
    svt::PopupWindowController( rxContext, css::uno::Reference< css::frame::XFrame >(), OUString() ),
262
0
    mePageType( NumberingPageType::SINGLENUM )
263
0
{
264
0
}
265
266
std::unique_ptr<WeldToolbarPopup> NumberingToolBoxControl::weldPopupWindow()
267
0
{
268
0
    return std::make_unique<NumberingPopup>(*this, m_pToolbar, mePageType);
269
0
}
270
271
VclPtr<vcl::Window> NumberingToolBoxControl::createVclPopupWindow( vcl::Window* pParent )
272
0
{
273
0
    mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
274
0
        std::make_unique<NumberingPopup>(*this, pParent->GetFrameWeld(), mePageType));
275
276
0
    mxInterimPopover->Show();
277
278
0
    return mxInterimPopover;
279
0
}
280
281
void SAL_CALL NumberingToolBoxControl::initialize( const css::uno::Sequence< css::uno::Any >& rArguments )
282
0
{
283
0
    svt::PopupWindowController::initialize( rArguments );
284
285
0
    if ( m_aCommandURL == ".uno:DefaultBullet" )
286
0
        mePageType = NumberingPageType::BULLET;
287
0
    else if ( m_aCommandURL == ".uno:SetOutline" )
288
0
        mePageType = NumberingPageType::OUTLINE;
289
290
0
    if (m_pToolbar)
291
0
    {
292
0
        mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar));
293
0
        m_pToolbar->set_item_popover(m_aCommandURL, mxPopoverContainer->getTopLevel());
294
0
        return;
295
0
    }
296
297
0
    ToolBox* pToolBox = nullptr;
298
0
    ToolBoxItemId nId;
299
0
    if (getToolboxId(nId, &pToolBox))
300
0
    {
301
0
        ToolBoxItemBits nBits = mePageType == NumberingPageType::OUTLINE
302
0
                                    ? ToolBoxItemBits::DROPDOWNONLY
303
0
                                    : ToolBoxItemBits::DROPDOWN;
304
0
        pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | nBits );
305
0
    }
306
0
}
307
308
OUString SAL_CALL NumberingToolBoxControl::getImplementationName()
309
0
{
310
0
    return u"com.sun.star.comp.svx.NumberingToolBoxControl"_ustr;
311
0
}
312
313
css::uno::Sequence< OUString > SAL_CALL NumberingToolBoxControl::getSupportedServiceNames()
314
0
{
315
0
    return { u"com.sun.star.frame.ToolbarController"_ustr };
316
0
}
317
318
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
319
com_sun_star_comp_svx_NumberingToolBoxControl_get_implementation(
320
    css::uno::XComponentContext *rxContext,
321
    css::uno::Sequence<css::uno::Any> const & )
322
0
{
323
0
    return cppu::acquire( new NumberingToolBoxControl( rxContext ) );
324
0
}
325
326
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */