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