Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/chart2/source/controller/sidebar/ChartErrorBarPanel.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
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <com/sun/star/chart/ErrorBarStyle.hpp>
21
#include <com/sun/star/beans/XPropertySet.hpp>
22
23
#include "ChartErrorBarPanel.hxx"
24
#include <ChartController.hxx>
25
#include <ChartModel.hxx>
26
#include <vcl/svapp.hxx>
27
#include <vcl/weld/Builder.hxx>
28
#include <sal/log.hxx>
29
30
31
using namespace css;
32
using namespace css::uno;
33
34
namespace chart::sidebar {
35
36
namespace {
37
38
enum class ErrorBarDirection
39
{
40
    POSITIVE,
41
    NEGATIVE
42
};
43
44
css::uno::Reference<css::beans::XPropertySet> getErrorBarPropSet(
45
        const rtl::Reference<::chart::ChartModel>& xModel, std::u16string_view rCID)
46
0
{
47
0
    return ObjectIdentifier::getObjectPropertySet(rCID, xModel);
48
0
}
49
50
bool showPositiveError(const rtl::Reference<::chart::ChartModel>& xModel,
51
        std::u16string_view rCID)
52
0
{
53
0
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
54
0
        getErrorBarPropSet(xModel, rCID);
55
56
0
    if (!xPropSet.is())
57
0
        return false;
58
59
0
    css::uno::Any aAny = xPropSet->getPropertyValue(u"ShowPositiveError"_ustr);
60
61
0
    if (!aAny.hasValue())
62
0
        return false;
63
64
0
    bool bShow = false;
65
0
    aAny >>= bShow;
66
0
    return bShow;
67
0
}
68
69
bool showNegativeError(const rtl::Reference<::chart::ChartModel>& xModel,
70
        std::u16string_view rCID)
71
0
{
72
0
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
73
0
        getErrorBarPropSet(xModel, rCID);
74
75
0
    if (!xPropSet.is())
76
0
        return false;
77
78
0
    css::uno::Any aAny = xPropSet->getPropertyValue(u"ShowNegativeError"_ustr);
79
80
0
    if (!aAny.hasValue())
81
0
        return false;
82
83
0
    bool bShow = false;
84
0
    aAny >>= bShow;
85
0
    return bShow;
86
0
}
87
88
void setShowPositiveError(const rtl::Reference<::chart::ChartModel>& xModel,
89
        std::u16string_view rCID, bool bShow)
90
0
{
91
0
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
92
0
        getErrorBarPropSet(xModel, rCID);
93
94
0
    if (!xPropSet.is())
95
0
        return;
96
97
0
    xPropSet->setPropertyValue(u"ShowPositiveError"_ustr, css::uno::Any(bShow));
98
0
}
99
100
void setShowNegativeError(const rtl::Reference<::chart::ChartModel>& xModel,
101
        std::u16string_view rCID, bool bShow)
102
0
{
103
0
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
104
0
        getErrorBarPropSet(xModel, rCID);
105
106
0
    if (!xPropSet.is())
107
0
        return;
108
109
0
    xPropSet->setPropertyValue(u"ShowNegativeError"_ustr, css::uno::Any(bShow));
110
0
}
111
112
struct ErrorBarTypeMap
113
{
114
    sal_Int32 nPos;
115
    sal_Int32 nApi;
116
};
117
118
ErrorBarTypeMap const aErrorBarType[] = {
119
    { 0, css::chart::ErrorBarStyle::ABSOLUTE },
120
    { 1, css::chart::ErrorBarStyle::RELATIVE },
121
    { 2, css::chart::ErrorBarStyle::FROM_DATA },
122
    { 3, css::chart::ErrorBarStyle::STANDARD_DEVIATION },
123
    { 4, css::chart::ErrorBarStyle::STANDARD_ERROR },
124
    { 5, css::chart::ErrorBarStyle::VARIANCE},
125
    { 6, css::chart::ErrorBarStyle::ERROR_MARGIN },
126
};
127
128
sal_Int32 getTypePos(const rtl::Reference<::chart::ChartModel>& xModel,
129
        std::u16string_view rCID)
130
0
{
131
0
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
132
0
        getErrorBarPropSet(xModel, rCID);
133
134
0
    if (!xPropSet.is())
135
0
        return 0;
136
137
0
    css::uno::Any aAny = xPropSet->getPropertyValue(u"ErrorBarStyle"_ustr);
138
139
0
    if (!aAny.hasValue())
140
0
        return 0;
141
142
0
    sal_Int32 nApi = 0;
143
0
    aAny >>= nApi;
144
145
0
    for (ErrorBarTypeMap const & i : aErrorBarType)
146
0
    {
147
0
        if (i.nApi == nApi)
148
0
            return i.nPos;
149
0
    }
150
151
0
    return 0;
152
0
}
153
154
void setTypePos(const rtl::Reference<::chart::ChartModel>& xModel,
155
        std::u16string_view rCID, sal_Int32 nPos)
156
0
{
157
0
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
158
0
        getErrorBarPropSet(xModel, rCID);
159
160
0
    if (!xPropSet.is())
161
0
        return;
162
163
0
    sal_Int32 nApi = 0;
164
0
    for (ErrorBarTypeMap const & i : aErrorBarType)
165
0
    {
166
0
        if (i.nPos == nPos)
167
0
            nApi = i.nApi;
168
0
    }
169
170
0
    xPropSet->setPropertyValue(u"ErrorBarStyle"_ustr, css::uno::Any(nApi));
171
0
}
172
173
double getValue(const rtl::Reference<::chart::ChartModel>& xModel,
174
        std::u16string_view rCID, ErrorBarDirection eDir)
175
0
{
176
0
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
177
0
        getErrorBarPropSet(xModel, rCID);
178
179
0
    if (!xPropSet.is())
180
0
        return 0;
181
182
0
    OUString aName = u"PositiveError"_ustr;
183
0
    if (eDir == ErrorBarDirection::NEGATIVE)
184
0
        aName = "NegativeError";
185
186
0
    css::uno::Any aAny = xPropSet->getPropertyValue(aName);
187
188
0
    if (!aAny.hasValue())
189
0
        return 0;
190
191
0
    double nVal = 0;
192
0
    aAny >>= nVal;
193
194
0
    return nVal;
195
0
}
196
197
void setValue(const rtl::Reference<::chart::ChartModel>& xModel,
198
        std::u16string_view rCID, double nVal, ErrorBarDirection eDir)
199
0
{
200
0
    css::uno::Reference<css::beans::XPropertySet> xPropSet =
201
0
        getErrorBarPropSet(xModel, rCID);
202
203
0
    if (!xPropSet.is())
204
0
        return;
205
206
0
    OUString aName = u"PositiveError"_ustr;
207
0
    if (eDir == ErrorBarDirection::NEGATIVE)
208
0
        aName = "NegativeError";
209
210
0
    xPropSet->setPropertyValue(aName, css::uno::Any(nVal));
211
0
}
212
213
OUString getCID(const rtl::Reference<::chart::ChartModel>& xModel)
214
0
{
215
0
    css::uno::Reference<css::frame::XController> xController(xModel->getCurrentController());
216
0
    css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier(xController, css::uno::UNO_QUERY);
217
0
    if (!xSelectionSupplier.is())
218
0
        return OUString();
219
220
0
    uno::Any aAny = xSelectionSupplier->getSelection();
221
0
    assert(aAny.hasValue());
222
0
    OUString aCID;
223
0
    aAny >>= aCID;
224
#if defined DBG_UTIL && !defined NDEBUG
225
    ObjectType eType = ObjectIdentifier::getObjectType(aCID);
226
    if (eType != OBJECTTYPE_DATA_ERRORS_X &&
227
         eType != OBJECTTYPE_DATA_ERRORS_Y &&
228
         eType != OBJECTTYPE_DATA_ERRORS_Z)
229
        SAL_WARN("chart2","Selected item is not an error bar");
230
231
#endif
232
233
0
    return aCID;
234
0
}
235
236
}
237
238
ChartErrorBarPanel::ChartErrorBarPanel(weld::Widget* pParent, ChartController* pController)
239
0
    : PanelLayout(pParent, u"ChartErrorBarPanel"_ustr, u"modules/schart/ui/sidebarerrorbar.ui"_ustr)
240
0
    , mxRBPosAndNeg(m_xBuilder->weld_radio_button(u"radiobutton_positive_negative"_ustr))
241
0
    , mxRBPos(m_xBuilder->weld_radio_button(u"radiobutton_positive"_ustr))
242
0
    , mxRBNeg(m_xBuilder->weld_radio_button(u"radiobutton_negative"_ustr))
243
0
    , mxLBType(m_xBuilder->weld_combo_box(u"comboboxtext_type"_ustr))
244
0
    , mxMFPos(m_xBuilder->weld_spin_button(u"spinbutton_pos"_ustr))
245
0
    , mxMFNeg(m_xBuilder->weld_spin_button(u"spinbutton_neg"_ustr))
246
0
    , mxModel(pController->getChartModel())
247
0
    , mxListener(new ChartSidebarModifyListener(this))
248
0
    , mbModelValid(true)
249
0
{
250
0
    Initialize();
251
0
}
252
253
ChartErrorBarPanel::~ChartErrorBarPanel()
254
0
{
255
0
    doUpdateModel(nullptr);
256
257
0
    mxRBPosAndNeg.reset();
258
0
    mxRBPos.reset();
259
0
    mxRBNeg.reset();
260
261
0
    mxLBType.reset();
262
263
0
    mxMFPos.reset();
264
0
    mxMFNeg.reset();
265
0
}
266
267
void ChartErrorBarPanel::Initialize()
268
0
{
269
0
    mxModel->addModifyListener(mxListener);
270
0
    mxRBNeg->set_active(false);
271
0
    mxRBPos->set_active(false);
272
0
    mxRBPosAndNeg->set_active(false);
273
274
0
    updateData();
275
276
0
    Link<weld::Toggleable&,void> aLink = LINK(this, ChartErrorBarPanel, RadioBtnHdl);
277
0
    mxRBPosAndNeg->connect_toggled(aLink);
278
0
    mxRBPos->connect_toggled(aLink);
279
0
    mxRBNeg->connect_toggled(aLink);
280
281
0
    mxLBType->connect_changed(LINK(this, ChartErrorBarPanel, ListBoxHdl));
282
283
0
    Link<weld::SpinButton&,void> aLink2 = LINK(this, ChartErrorBarPanel, NumericFieldHdl);
284
0
    mxMFPos->connect_value_changed(aLink2);
285
0
    mxMFNeg->connect_value_changed(aLink2);
286
0
}
287
288
void ChartErrorBarPanel::updateData()
289
0
{
290
0
    if (!mbModelValid)
291
0
        return;
292
293
0
    OUString aCID = getCID(mxModel);
294
0
    ObjectType eType = ObjectIdentifier::getObjectType(aCID);
295
0
    if (eType != OBJECTTYPE_DATA_ERRORS_X &&
296
0
         eType != OBJECTTYPE_DATA_ERRORS_Y &&
297
0
         eType != OBJECTTYPE_DATA_ERRORS_Z)
298
0
        return;
299
300
0
    bool bPos = showPositiveError(mxModel, aCID);
301
0
    bool bNeg = showNegativeError(mxModel, aCID);
302
303
0
    SolarMutexGuard aGuard;
304
305
0
    if (bPos && bNeg)
306
0
        mxRBPosAndNeg->set_active(true);
307
0
    else if (bPos)
308
0
        mxRBPos->set_active(true);
309
0
    else if (bNeg)
310
0
        mxRBNeg->set_active(true);
311
312
0
    sal_Int32 nTypePos = getTypePos(mxModel, aCID);
313
0
    mxLBType->set_active(nTypePos);
314
315
0
    if (nTypePos <= 1)
316
0
    {
317
0
        if (bPos)
318
0
            mxMFPos->set_sensitive(true);
319
0
        else
320
0
            mxMFPos->set_sensitive(false);
321
322
0
        if (bNeg)
323
0
            mxMFNeg->set_sensitive(true);
324
0
        else
325
0
            mxMFNeg->set_sensitive(false);
326
327
0
        double nValPos = getValue(mxModel, aCID, ErrorBarDirection::POSITIVE);
328
0
        double nValNeg = getValue(mxModel, aCID, ErrorBarDirection::NEGATIVE);
329
330
0
        mxMFPos->set_value(nValPos);
331
0
        mxMFNeg->set_value(nValNeg);
332
0
    }
333
0
    else
334
0
    {
335
0
        mxMFPos->set_sensitive(false);
336
0
        mxMFNeg->set_sensitive(false);
337
0
    }
338
0
}
339
340
std::unique_ptr<PanelLayout> ChartErrorBarPanel::Create (
341
    weld::Widget* pParent,
342
    ChartController* pController)
343
0
{
344
0
    if (pParent == nullptr)
345
0
        throw lang::IllegalArgumentException(u"no parent Window given to ChartErrorBarPanel::Create"_ustr, nullptr, 0);
346
0
    return std::make_unique<ChartErrorBarPanel>(pParent, pController);
347
0
}
348
349
void ChartErrorBarPanel::DataChanged(const DataChangedEvent& rEvent)
350
0
{
351
0
    PanelLayout::DataChanged(rEvent);
352
0
    updateData();
353
0
}
354
355
void ChartErrorBarPanel::HandleContextChange(
356
    const vcl::EnumContext& )
357
0
{
358
0
    updateData();
359
0
}
360
361
void ChartErrorBarPanel::NotifyItemUpdate(
362
    sal_uInt16 /*nSID*/,
363
    SfxItemState /*eState*/,
364
    const SfxPoolItem* /*pState*/ )
365
0
{
366
0
}
367
368
void ChartErrorBarPanel::modelInvalid()
369
0
{
370
0
    mbModelValid = false;
371
0
}
372
373
void ChartErrorBarPanel::doUpdateModel(const rtl::Reference<::chart::ChartModel>& xModel)
374
0
{
375
0
    if (mbModelValid)
376
0
    {
377
0
        mxModel->removeModifyListener(mxListener);
378
0
    }
379
380
0
    mxModel = xModel;
381
0
    mbModelValid = mxModel.is();
382
383
0
    if (!mbModelValid)
384
0
        return;
385
386
0
    mxModel->addModifyListener(mxListener);
387
0
}
388
389
void ChartErrorBarPanel::updateModel(css::uno::Reference<css::frame::XModel> xModel)
390
0
{
391
0
    ::chart::ChartModel* pModel = dynamic_cast<::chart::ChartModel*>(xModel.get());
392
0
    assert(!xModel || pModel);
393
0
    doUpdateModel(pModel);
394
0
}
395
396
IMPL_LINK_NOARG(ChartErrorBarPanel, RadioBtnHdl, weld::Toggleable&, void)
397
0
{
398
0
    OUString aCID = getCID(mxModel);
399
0
    bool bPos = mxRBPosAndNeg->get_active() || mxRBPos->get_active();
400
0
    bool bNeg = mxRBPosAndNeg->get_active() || mxRBNeg->get_active();
401
402
0
    setShowPositiveError(mxModel, aCID, bPos);
403
0
    setShowNegativeError(mxModel, aCID, bNeg);
404
0
}
405
406
IMPL_LINK_NOARG(ChartErrorBarPanel, ListBoxHdl, weld::ComboBox&, void)
407
0
{
408
0
    OUString aCID = getCID(mxModel);
409
0
    sal_Int32 nPos = mxLBType->get_active();
410
411
0
    setTypePos(mxModel, aCID, nPos);
412
0
}
413
414
IMPL_LINK(ChartErrorBarPanel, NumericFieldHdl, weld::SpinButton&, rMetricField, void)
415
0
{
416
0
    OUString aCID = getCID(mxModel);
417
0
    double nVal = rMetricField.get_value();
418
0
    if (&rMetricField == mxMFPos.get())
419
0
        setValue(mxModel, aCID, nVal, ErrorBarDirection::POSITIVE);
420
0
    else if (&rMetricField == mxMFNeg.get())
421
0
        setValue(mxModel, aCID, nVal, ErrorBarDirection::NEGATIVE);
422
0
}
423
424
} // end of namespace ::chart::sidebar
425
426
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */