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