Coverage Report

Created: 2025-07-07 10:01

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