Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/chart2/source/model/main/DataSeries.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 <DataSeries.hxx>
21
#include <DataSeriesProperties.hxx>
22
#include "DataPointProperties.hxx"
23
#include <CharacterProperties.hxx>
24
#include <UserDefinedProperties.hxx>
25
#include "DataPoint.hxx"
26
#include <DataSeriesHelper.hxx>
27
#include <CloneHelper.hxx>
28
#include <RegressionCurveModel.hxx>
29
#include <ModifyListenerHelper.hxx>
30
#include <unonames.hxx>
31
#include <com/sun/star/chart2/DataPointLabel.hpp>
32
#include <com/sun/star/chart2/Symbol.hpp>
33
#include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
34
#include <com/sun/star/container/NoSuchElementException.hpp>
35
#include <com/sun/star/drawing/LineStyle.hpp>
36
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
37
#include <cppuhelper/supportsservice.hxx>
38
#include <comphelper/diagnose_ex.hxx>
39
#include <rtl/ref.hxx>
40
#include <rtl/ustrbuf.hxx>
41
42
#include <algorithm>
43
44
namespace com::sun::star::uno { class XComponentContext; }
45
46
using namespace ::com::sun::star;
47
48
using ::com::sun::star::beans::Property;
49
using ::com::sun::star::uno::Sequence;
50
using ::com::sun::star::uno::Reference;
51
using ::osl::MutexGuard;
52
53
namespace chart
54
{
55
const ::chart::tPropertyValueMap & StaticDataSeriesDefaults()
56
0
{
57
0
    static const ::chart::tPropertyValueMap aStaticDefaults = []()
58
0
        {
59
0
            ::chart::tPropertyValueMap aMap;
60
0
            ::chart::DataSeriesProperties::AddDefaultsToMap( aMap );
61
0
            ::chart::CharacterProperties::AddDefaultsToMap( aMap );
62
0
            float fDefaultCharHeight = 10.0;
63
0
            ::chart::PropertyHelper::setPropertyValue( aMap, ::chart::CharacterProperties::PROP_CHAR_CHAR_HEIGHT, fDefaultCharHeight );
64
0
            ::chart::PropertyHelper::setPropertyValue( aMap, ::chart::CharacterProperties::PROP_CHAR_ASIAN_CHAR_HEIGHT, fDefaultCharHeight );
65
0
            ::chart::PropertyHelper::setPropertyValue( aMap, ::chart::CharacterProperties::PROP_CHAR_COMPLEX_CHAR_HEIGHT, fDefaultCharHeight );
66
0
            return aMap;
67
0
        }();
68
0
    return aStaticDefaults;
69
0
};
70
} // namespace chart
71
72
namespace
73
{
74
75
::cppu::OPropertyArrayHelper& StaticDataSeriesInfoHelper()
76
0
{
77
0
    static ::cppu::OPropertyArrayHelper oHelper = []()
78
0
        {
79
0
            std::vector< css::beans::Property > aProperties;
80
0
            ::chart::DataSeriesProperties::AddPropertiesToVector( aProperties );
81
0
            ::chart::CharacterProperties::AddPropertiesToVector( aProperties );
82
0
            ::chart::UserDefinedProperties::AddPropertiesToVector( aProperties );
83
84
0
            std::sort( aProperties.begin(), aProperties.end(),
85
0
                         ::chart::PropertyNameLess() );
86
87
0
            return comphelper::containerToSequence( aProperties );
88
0
        }();
89
0
    return oHelper;
90
0
};
91
92
void lcl_SetParent(
93
    const uno::Reference< uno::XInterface > & xChildInterface,
94
    const uno::Reference< uno::XInterface > & xParentInterface )
95
0
{
96
0
    uno::Reference< container::XChild > xChild( xChildInterface, uno::UNO_QUERY );
97
0
    if( xChild.is())
98
0
        xChild->setParent( xParentInterface );
99
0
}
100
101
typedef std::map< sal_Int32, css::uno::Reference< css::beans::XPropertySet > >
102
    lcl_tDataPointMap;
103
104
void lcl_CloneAttributedDataPoints(
105
    const lcl_tDataPointMap & rSource, lcl_tDataPointMap & rDestination,
106
    const uno::Reference< uno::XInterface > & xSeries )
107
0
{
108
0
    for (auto const& elem : rSource)
109
0
    {
110
0
        Reference< beans::XPropertySet > xPoint( elem.second );
111
0
        if( xPoint.is())
112
0
        {
113
0
            Reference< util::XCloneable > xCloneable( xPoint, uno::UNO_QUERY );
114
0
            if( xCloneable.is())
115
0
            {
116
0
                xPoint.set( xCloneable->createClone(), uno::UNO_QUERY );
117
0
                if( xPoint.is())
118
0
                {
119
0
                    lcl_SetParent( xPoint, xSeries );
120
0
                    rDestination.emplace( elem.first, xPoint );
121
0
                }
122
0
            }
123
0
        }
124
0
    }
125
0
}
126
127
} // anonymous namespace
128
129
namespace chart
130
{
131
132
DataSeries::DataSeries() :
133
0
        m_xModifyEventForwarder( new ModifyEventForwarder() )
134
0
{
135
0
}
136
137
DataSeries::DataSeries( const DataSeries & rOther ) :
138
0
        impl::DataSeries_Base(rOther),
139
0
        ::property::OPropertySet( rOther ),
140
0
    m_xModifyEventForwarder( new ModifyEventForwarder() )
141
0
{
142
0
    if( ! rOther.m_aDataSequences.empty())
143
0
    {
144
0
        CloneHelper::CloneRefVector(rOther.m_aDataSequences, m_aDataSequences );
145
0
        ModifyListenerHelper::addListenerToAllElements( m_aDataSequences, m_xModifyEventForwarder );
146
0
    }
147
148
0
    CloneHelper::CloneRefVector( rOther.m_aRegressionCurves, m_aRegressionCurves );
149
0
    ModifyListenerHelper::addListenerToAllElements( m_aRegressionCurves, m_xModifyEventForwarder );
150
151
    // add as listener to XPropertySet properties
152
0
    Reference< beans::XPropertySet > xPropertySet;
153
0
    uno::Any aValue;
154
155
0
    getFastPropertyValue( aValue, DataPointProperties::PROP_DATAPOINT_ERROR_BAR_X );
156
0
    if( ( aValue >>= xPropertySet )
157
0
        && xPropertySet.is())
158
0
        ModifyListenerHelper::addListener( xPropertySet, m_xModifyEventForwarder );
159
160
0
    getFastPropertyValue( aValue, DataPointProperties::PROP_DATAPOINT_ERROR_BAR_Y );
161
0
    if( ( aValue >>= xPropertySet )
162
0
        && xPropertySet.is())
163
0
        ModifyListenerHelper::addListener( xPropertySet, m_xModifyEventForwarder );
164
0
}
165
166
// late initialization to call after copy-constructing
167
void DataSeries::Init( const DataSeries & rOther )
168
0
{
169
0
    Reference< uno::XInterface > xThisInterface( static_cast< ::cppu::OWeakObject * >( this ));
170
0
    if( ! rOther.m_aAttributedDataPoints.empty())
171
0
    {
172
0
        lcl_CloneAttributedDataPoints(
173
0
            rOther.m_aAttributedDataPoints, m_aAttributedDataPoints, xThisInterface );
174
0
        ModifyListenerHelper::addListenerToAllMapElements( m_aAttributedDataPoints, m_xModifyEventForwarder );
175
0
    }
176
177
    // add as parent to error bars
178
0
    Reference< beans::XPropertySet > xPropertySet;
179
0
    uno::Any aValue;
180
181
0
    getFastPropertyValue( aValue, DataPointProperties::PROP_DATAPOINT_ERROR_BAR_X );
182
0
    if( ( aValue >>= xPropertySet )
183
0
        && xPropertySet.is())
184
0
        lcl_SetParent( xPropertySet, xThisInterface );
185
186
0
    getFastPropertyValue( aValue, DataPointProperties::PROP_DATAPOINT_ERROR_BAR_Y );
187
0
    if( ( aValue >>= xPropertySet )
188
0
        && xPropertySet.is())
189
0
        lcl_SetParent( xPropertySet, xThisInterface );
190
0
}
191
192
DataSeries::~DataSeries()
193
0
{
194
0
    try
195
0
    {
196
0
        ModifyListenerHelper::removeListenerFromAllMapElements( m_aAttributedDataPoints, m_xModifyEventForwarder );
197
0
        ModifyListenerHelper::removeListenerFromAllElements( m_aRegressionCurves, m_xModifyEventForwarder );
198
0
        ModifyListenerHelper::removeListenerFromAllElements( m_aDataSequences, m_xModifyEventForwarder );
199
200
        // remove listener from XPropertySet properties
201
0
        Reference< beans::XPropertySet > xPropertySet;
202
0
        uno::Any aValue;
203
204
0
        getFastPropertyValue( aValue, DataPointProperties::PROP_DATAPOINT_ERROR_BAR_X );
205
0
        if( ( aValue >>= xPropertySet )
206
0
            && xPropertySet.is())
207
0
            ModifyListenerHelper::removeListener( xPropertySet, m_xModifyEventForwarder );
208
209
0
        getFastPropertyValue( aValue, DataPointProperties::PROP_DATAPOINT_ERROR_BAR_Y );
210
0
        if( ( aValue >>= xPropertySet )
211
0
            && xPropertySet.is())
212
0
            ModifyListenerHelper::removeListener( xPropertySet, m_xModifyEventForwarder );
213
0
    }
214
0
    catch( const uno::Exception & )
215
0
    {
216
0
        DBG_UNHANDLED_EXCEPTION("chart2");
217
0
    }
218
0
}
219
220
// ____ XCloneable ____
221
uno::Reference< util::XCloneable > SAL_CALL DataSeries::createClone()
222
0
{
223
0
    rtl::Reference<DataSeries> pNewSeries( new DataSeries( *this ));
224
    // do initialization that uses uno references to the clone
225
0
    pNewSeries->Init( *this );
226
227
0
    return pNewSeries;
228
0
}
229
230
// ____ OPropertySet ____
231
void DataSeries::GetDefaultValue( sal_Int32 nHandle, uno::Any& rDest ) const
232
0
{
233
0
    const tPropertyValueMap& rStaticDefaults = StaticDataSeriesDefaults();
234
0
    tPropertyValueMap::const_iterator aFound( rStaticDefaults.find( nHandle ) );
235
0
    if( aFound == rStaticDefaults.end() )
236
0
        rDest.clear();
237
0
    else
238
0
        rDest = (*aFound).second;
239
0
}
240
241
// ____ OPropertySet ____
242
::cppu::IPropertyArrayHelper & SAL_CALL DataSeries::getInfoHelper()
243
0
{
244
0
    return StaticDataSeriesInfoHelper();
245
0
}
246
247
// ____ XPropertySet ____
248
uno::Reference< beans::XPropertySetInfo > SAL_CALL DataSeries::getPropertySetInfo()
249
0
{
250
0
    static uno::Reference< beans::XPropertySetInfo > xPropSetInfo =
251
0
        ::cppu::OPropertySetHelper::createPropertySetInfo(StaticDataSeriesInfoHelper() );
252
0
    return xPropSetInfo;
253
0
}
254
255
void SAL_CALL DataSeries::getFastPropertyValue
256
    ( uno::Any& rValue,
257
      sal_Int32 nHandle ) const
258
0
{
259
    // special handling for get.  set is not possible for this property
260
0
    if( nHandle == DataSeriesProperties::PROP_DATASERIES_ATTRIBUTED_DATA_POINTS )
261
0
    {
262
        // TODO: only add those property sets that are really modified
263
264
0
        rValue <<= comphelper::mapKeysToSequence(m_aAttributedDataPoints);
265
0
    }
266
0
    else
267
0
        OPropertySet::getFastPropertyValue( rValue, nHandle );
268
0
}
269
270
void SAL_CALL DataSeries::setFastPropertyValue_NoBroadcast(
271
    sal_Int32 nHandle, const uno::Any& rValue )
272
0
{
273
0
    if(    nHandle == DataPointProperties::PROP_DATAPOINT_ERROR_BAR_Y
274
0
        || nHandle == DataPointProperties::PROP_DATAPOINT_ERROR_BAR_X )
275
0
    {
276
0
        uno::Any aOldValue;
277
0
        Reference< util::XModifyBroadcaster > xBroadcaster;
278
0
        getFastPropertyValue( aOldValue, nHandle );
279
0
        if( aOldValue.hasValue() &&
280
0
            (aOldValue >>= xBroadcaster) &&
281
0
            xBroadcaster.is())
282
0
        {
283
0
            ModifyListenerHelper::removeListener( xBroadcaster, m_xModifyEventForwarder );
284
0
        }
285
286
0
        OSL_ASSERT( rValue.getValueTypeClass() == uno::TypeClass_INTERFACE );
287
0
        if( rValue.hasValue() &&
288
0
            (rValue >>= xBroadcaster) &&
289
0
            xBroadcaster.is())
290
0
        {
291
0
            ModifyListenerHelper::addListener( xBroadcaster, m_xModifyEventForwarder );
292
0
        }
293
0
    }
294
295
0
    ::property::OPropertySet::setFastPropertyValue_NoBroadcast( nHandle, rValue );
296
0
}
297
298
Reference< beans::XPropertySet >
299
    SAL_CALL DataSeries::getDataPointByIndex( sal_Int32 nIndex )
300
0
{
301
0
    Reference< beans::XPropertySet > xResult;
302
303
0
    std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSequences;
304
0
    {
305
0
        MutexGuard aGuard( m_aMutex );
306
0
        aSequences = m_aDataSequences;
307
0
    }
308
309
0
    std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aValuesSeries(
310
0
        DataSeriesHelper::getAllDataSequencesByRole( aSequences , u"values"_ustr ) );
311
312
0
    if (aValuesSeries.empty())
313
0
        throw lang::IndexOutOfBoundsException();
314
315
0
    Reference< chart2::data::XDataSequence > xSeq( aValuesSeries.front()->getValues() );
316
0
    if( 0 <= nIndex && nIndex < xSeq->getData().getLength() )
317
0
    {
318
0
        {
319
0
            MutexGuard aGuard( m_aMutex );
320
0
            tDataPointAttributeContainer::iterator aIt( m_aAttributedDataPoints.find( nIndex ) );
321
0
            if( aIt != m_aAttributedDataPoints.end() )
322
0
                xResult = (*aIt).second;
323
0
        }
324
0
        if( !xResult.is() )
325
0
        {
326
0
            Reference< beans::XPropertySet > xParentProperties;
327
0
            rtl::Reference< ModifyEventForwarder > xModifyEventForwarder;
328
0
            {
329
0
                MutexGuard aGuard( m_aMutex );
330
0
                xParentProperties = this;
331
0
                xModifyEventForwarder = m_xModifyEventForwarder;
332
0
            }
333
334
            // create a new XPropertySet for this data point
335
0
            xResult.set( new DataPoint( xParentProperties ) );
336
0
            {
337
0
                MutexGuard aGuard( m_aMutex );
338
0
                m_aAttributedDataPoints[ nIndex ] = xResult;
339
0
            }
340
0
            ModifyListenerHelper::addListener( xResult, xModifyEventForwarder );
341
0
        }
342
0
    }
343
344
0
    return xResult;
345
0
}
346
347
void SAL_CALL DataSeries::resetDataPoint( sal_Int32 nIndex )
348
0
{
349
0
    Reference< beans::XPropertySet > xDataPointProp;
350
0
    rtl::Reference< ModifyEventForwarder > xModifyEventForwarder;
351
0
    {
352
0
        MutexGuard aGuard( m_aMutex );
353
0
        xModifyEventForwarder = m_xModifyEventForwarder;
354
0
        tDataPointAttributeContainer::iterator aIt( m_aAttributedDataPoints.find( nIndex ));
355
0
        if( aIt != m_aAttributedDataPoints.end())
356
0
        {
357
0
            xDataPointProp = (*aIt).second;
358
0
            m_aAttributedDataPoints.erase(aIt);
359
0
        }
360
361
0
    }
362
0
    if( xDataPointProp.is() )
363
0
    {
364
0
        Reference< util::XModifyBroadcaster > xBroadcaster( xDataPointProp, uno::UNO_QUERY );
365
0
        if( xBroadcaster.is() && xModifyEventForwarder.is())
366
0
            xBroadcaster->removeModifyListener( xModifyEventForwarder );
367
0
        fireModifyEvent();
368
0
    }
369
0
}
370
371
void SAL_CALL DataSeries::resetAllDataPoints()
372
0
{
373
0
    tDataPointAttributeContainer  aOldAttributedDataPoints;
374
0
    rtl::Reference< ModifyEventForwarder > xModifyEventForwarder;
375
0
    {
376
0
        MutexGuard aGuard( m_aMutex );
377
0
        xModifyEventForwarder = m_xModifyEventForwarder;
378
0
        std::swap( aOldAttributedDataPoints, m_aAttributedDataPoints );
379
0
    }
380
0
    ModifyListenerHelper::removeListenerFromAllMapElements( aOldAttributedDataPoints, xModifyEventForwarder );
381
0
    aOldAttributedDataPoints.clear();
382
0
    fireModifyEvent();
383
0
}
384
385
// ____ XDataSink ____
386
void SAL_CALL DataSeries::setData( const uno::Sequence< Reference< chart2::data::XLabeledDataSequence > >& aData )
387
0
{
388
0
    tDataSequenceContainer aOldDataSequences;
389
0
    tDataSequenceContainer aNewDataSequences;
390
0
    rtl::Reference< ModifyEventForwarder > xModifyEventForwarder;
391
0
    {
392
0
        MutexGuard aGuard( m_aMutex );
393
0
        xModifyEventForwarder = m_xModifyEventForwarder;
394
0
        std::swap( aOldDataSequences, m_aDataSequences );
395
0
        for (const auto & i : aData)
396
0
        {
397
0
            aNewDataSequences.push_back(i);
398
0
        }
399
0
        m_aDataSequences = aNewDataSequences;
400
0
    }
401
0
    ModifyListenerHelper::removeListenerFromAllElements( aOldDataSequences, xModifyEventForwarder );
402
0
    ModifyListenerHelper::addListenerToAllElements( aNewDataSequences, xModifyEventForwarder );
403
0
    fireModifyEvent();
404
0
}
405
406
void DataSeries::setData( const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > >& aData )
407
0
{
408
0
    tDataSequenceContainer aOldDataSequences;
409
0
    tDataSequenceContainer aNewDataSequences;
410
0
    rtl::Reference< ModifyEventForwarder > xModifyEventForwarder;
411
0
    {
412
0
        MutexGuard aGuard( m_aMutex );
413
0
        xModifyEventForwarder = m_xModifyEventForwarder;
414
0
        std::swap( aOldDataSequences, m_aDataSequences );
415
0
        aNewDataSequences = aData;
416
0
        m_aDataSequences = aNewDataSequences;
417
0
    }
418
0
    ModifyListenerHelper::removeListenerFromAllElements( aOldDataSequences, xModifyEventForwarder );
419
0
    ModifyListenerHelper::addListenerToAllElements( aNewDataSequences, xModifyEventForwarder );
420
0
    fireModifyEvent();
421
0
}
422
423
void DataSeries::addDataSequence(css::uno::Reference<css::chart2::data::XLabeledDataSequence> const& rSequence)
424
0
{
425
0
    m_aDataSequences.push_back(rSequence);
426
0
}
427
428
// ____ XDataSource ____
429
Sequence< Reference< chart2::data::XLabeledDataSequence > > SAL_CALL DataSeries::getDataSequences()
430
0
{
431
0
    MutexGuard aGuard( m_aMutex );
432
0
    return comphelper::containerToSequence<Reference< chart2::data::XLabeledDataSequence >>( m_aDataSequences );
433
0
}
434
435
// ____ XRegressionCurveContainer ____
436
void SAL_CALL DataSeries::addRegressionCurve(
437
    const uno::Reference< chart2::XRegressionCurve >& xRegressionCurve )
438
0
{
439
0
    auto pRegressionCurve = dynamic_cast<RegressionCurveModel*>(xRegressionCurve.get());
440
0
    assert(pRegressionCurve);
441
0
    rtl::Reference< ModifyEventForwarder > xModifyEventForwarder;
442
0
    {
443
0
        MutexGuard aGuard( m_aMutex );
444
0
        xModifyEventForwarder = m_xModifyEventForwarder;
445
0
        if( std::find( m_aRegressionCurves.begin(), m_aRegressionCurves.end(), pRegressionCurve )
446
0
            != m_aRegressionCurves.end())
447
0
            throw lang::IllegalArgumentException(u"curve not found"_ustr, static_cast<cppu::OWeakObject*>(this), 1);
448
0
        m_aRegressionCurves.push_back( pRegressionCurve );
449
0
    }
450
0
    ModifyListenerHelper::addListener( rtl::Reference<RegressionCurveModel>(pRegressionCurve), xModifyEventForwarder );
451
0
    fireModifyEvent();
452
0
}
453
454
void SAL_CALL DataSeries::removeRegressionCurve(
455
    const uno::Reference< chart2::XRegressionCurve >& xRegressionCurve )
456
0
{
457
0
    if( !xRegressionCurve.is() )
458
0
        throw container::NoSuchElementException();
459
0
    auto pRegressionCurve = dynamic_cast<RegressionCurveModel*>(xRegressionCurve.get());
460
0
    assert(pRegressionCurve);
461
462
0
    rtl::Reference< ModifyEventForwarder > xModifyEventForwarder;
463
0
    {
464
0
        MutexGuard aGuard( m_aMutex );
465
0
        xModifyEventForwarder = m_xModifyEventForwarder;
466
0
        tRegressionCurveContainerType::iterator aIt(
467
0
            std::find( m_aRegressionCurves.begin(), m_aRegressionCurves.end(), pRegressionCurve ) );
468
0
        if( aIt == m_aRegressionCurves.end())
469
0
            throw container::NoSuchElementException(
470
0
                u"The given regression curve is no element of this series"_ustr,
471
0
                static_cast< uno::XWeak * >( this ));
472
0
        m_aRegressionCurves.erase( aIt );
473
0
    }
474
475
0
    ModifyListenerHelper::removeListener( rtl::Reference<RegressionCurveModel>(pRegressionCurve), xModifyEventForwarder );
476
0
    fireModifyEvent();
477
0
}
478
479
uno::Sequence< uno::Reference< chart2::XRegressionCurve > > SAL_CALL DataSeries::getRegressionCurves()
480
0
{
481
0
    MutexGuard aGuard( m_aMutex );
482
0
    return comphelper::containerToSequence<uno::Reference< chart2::XRegressionCurve >>( m_aRegressionCurves );
483
0
}
484
485
void SAL_CALL DataSeries::setRegressionCurves(
486
    const Sequence< Reference< chart2::XRegressionCurve > >& aRegressionCurves )
487
0
{
488
0
    tRegressionCurveContainerType aOldCurves;
489
0
    tRegressionCurveContainerType aNewCurves;
490
0
    for (const auto & i : aRegressionCurves)
491
0
    {
492
0
        auto pRegressionCurve = dynamic_cast<RegressionCurveModel*>(i.get());
493
0
        assert(pRegressionCurve);
494
0
        aNewCurves.push_back(pRegressionCurve);
495
0
    }
496
0
    rtl::Reference< ModifyEventForwarder > xModifyEventForwarder;
497
0
    {
498
0
        MutexGuard aGuard( m_aMutex );
499
0
        xModifyEventForwarder = m_xModifyEventForwarder;
500
0
        std::swap( aOldCurves, m_aRegressionCurves );
501
0
        m_aRegressionCurves = aNewCurves;
502
0
    }
503
0
    ModifyListenerHelper::removeListenerFromAllElements( aOldCurves, xModifyEventForwarder );
504
0
    ModifyListenerHelper::addListenerToAllElements( aNewCurves, xModifyEventForwarder );
505
0
    fireModifyEvent();
506
0
}
507
508
// ____ XModifyBroadcaster ____
509
void SAL_CALL DataSeries::addModifyListener( const Reference< util::XModifyListener >& aListener )
510
0
{
511
0
    m_xModifyEventForwarder->addModifyListener( aListener );
512
0
}
513
514
void SAL_CALL DataSeries::removeModifyListener( const Reference< util::XModifyListener >& aListener )
515
0
{
516
0
    m_xModifyEventForwarder->removeModifyListener( aListener );
517
0
}
518
519
// ____ XModifyListener ____
520
void SAL_CALL DataSeries::modified( const lang::EventObject& aEvent )
521
0
{
522
0
    m_xModifyEventForwarder->modified( aEvent );
523
0
}
524
525
// ____ XEventListener (base of XModifyListener) ____
526
void SAL_CALL DataSeries::disposing( const lang::EventObject& )
527
0
{
528
0
}
529
530
// ____ OPropertySet ____
531
void DataSeries::firePropertyChangeEvent()
532
0
{
533
0
    fireModifyEvent();
534
0
}
535
536
void DataSeries::fireModifyEvent()
537
0
{
538
0
    m_xModifyEventForwarder->modified( lang::EventObject( static_cast< uno::XWeak* >( this )));
539
0
}
540
541
using impl::DataSeries_Base;
542
using ::property::OPropertySet;
543
544
IMPLEMENT_FORWARD_XINTERFACE2( DataSeries, DataSeries_Base, OPropertySet )
545
IMPLEMENT_FORWARD_XTYPEPROVIDER2( DataSeries, DataSeries_Base, OPropertySet )
546
547
// implement XServiceInfo methods basing upon getSupportedServiceNames_Static
548
OUString SAL_CALL DataSeries::getImplementationName()
549
0
{
550
0
    return u"com.sun.star.comp.chart.DataSeries"_ustr;
551
0
}
552
553
sal_Bool SAL_CALL DataSeries::supportsService( const OUString& rServiceName )
554
0
{
555
0
    return cppu::supportsService(this, rServiceName);
556
0
}
557
558
css::uno::Sequence< OUString > SAL_CALL DataSeries::getSupportedServiceNames()
559
0
{
560
0
    return {
561
0
        u"com.sun.star.chart2.DataSeries"_ustr,
562
0
        u"com.sun.star.chart2.DataPointProperties"_ustr,
563
0
        u"com.sun.star.beans.PropertySet"_ustr };
564
0
}
565
566
static Reference< chart2::data::XLabeledDataSequence > lcl_findLSequenceWithOnlyLabel(
567
    const Sequence< Reference< chart2::data::XLabeledDataSequence > > & rDataSequences )
568
0
{
569
0
    Reference< chart2::data::XLabeledDataSequence > xResult;
570
571
0
    for( auto const & labeledData : rDataSequences )
572
0
    {
573
0
        OSL_ENSURE( labeledData.is(), "empty LabeledDataSequence" );
574
        // no values are set but a label exists
575
0
        if( labeledData.is() &&
576
0
            ( ! labeledData->getValues().is() &&
577
0
              labeledData->getLabel().is()))
578
0
        {
579
0
            xResult.set( labeledData );
580
0
            break;
581
0
        }
582
0
    }
583
584
0
    return xResult;
585
0
}
586
587
static OUString lcl_getDataSequenceLabel( const Reference< chart2::data::XDataSequence > & xSequence )
588
0
{
589
0
    OUString aResult;
590
591
0
    Reference< chart2::data::XTextualDataSequence > xTextSeq( xSequence, uno::UNO_QUERY );
592
0
    if( xTextSeq.is())
593
0
    {
594
0
        Sequence< OUString > aSeq( xTextSeq->getTextualData());
595
596
0
        const sal_Int32 nMax = aSeq.getLength() - 1;
597
0
        OUStringBuffer aBuf;
598
599
0
        for( sal_Int32 i = 0; i <= nMax; ++i )
600
0
        {
601
0
            aBuf.append( aSeq[i] );
602
0
            if( i < nMax )
603
0
                aBuf.append( ' ');
604
0
        }
605
0
        aResult = aBuf.makeStringAndClear();
606
0
    }
607
0
    else if( xSequence.is())
608
0
    {
609
0
        Sequence< uno::Any > aSeq( xSequence->getData());
610
611
0
        const sal_Int32 nMax = aSeq.getLength() - 1;
612
0
        OUString aVal;
613
0
        OUStringBuffer aBuf;
614
0
        double fNum = 0;
615
616
0
        for( sal_Int32 i = 0; i <= nMax; ++i )
617
0
        {
618
0
            if( aSeq[i] >>= aVal )
619
0
            {
620
0
                aBuf.append( aVal );
621
0
                if( i < nMax )
622
0
                    aBuf.append(  ' ');
623
0
            }
624
0
            else if( aSeq[ i ] >>= fNum )
625
0
            {
626
0
                aBuf.append( fNum );
627
0
                if( i < nMax )
628
0
                    aBuf.append( ' ');
629
0
            }
630
0
        }
631
0
        aResult = aBuf.makeStringAndClear();
632
0
    }
633
634
0
    return aResult;
635
0
}
636
637
static OUString getLabelForLabeledDataSequence(
638
    const Reference< chart2::data::XLabeledDataSequence > & xLabeledSeq )
639
0
{
640
0
    OUString aResult;
641
0
    if( xLabeledSeq.is())
642
0
    {
643
0
        Reference< chart2::data::XDataSequence > xSeq( xLabeledSeq->getLabel());
644
0
        if( xSeq.is() )
645
0
            aResult = lcl_getDataSequenceLabel( xSeq );
646
0
        if( !xSeq.is() || aResult.isEmpty() )
647
0
        {
648
            // no label set or label content is empty -> use auto-generated one
649
0
            Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues() );
650
0
            if( xValueSeq.is() )
651
0
            {
652
0
                Sequence< OUString > aLabels( xValueSeq->generateLabel(
653
0
                    chart2::data::LabelOrigin_SHORT_SIDE ) );
654
                // no labels returned is interpreted as: auto-generation not
655
                // supported by sequence
656
0
                if( aLabels.hasElements() )
657
0
                    aResult=aLabels[0];
658
0
                else
659
0
                {
660
                    //todo?: maybe use the index of the series as name
661
                    //but as the index may change it would be better to have such a name persistent
662
                    //what is not possible at the moment
663
                    //--> maybe use the identifier as part of the name ...
664
0
                    aResult = lcl_getDataSequenceLabel( xValueSeq );
665
0
                }
666
0
            }
667
0
        }
668
0
    }
669
0
    return aResult;
670
0
}
671
672
OUString DataSeries::getLabelForRole( const OUString & rLabelSequenceRole )
673
0
{
674
0
    OUString aResult;
675
676
0
    Reference< chart2::data::XLabeledDataSequence > xLabeledSeq(
677
0
        ::chart::DataSeriesHelper::getDataSequenceByRole( this, rLabelSequenceRole ));
678
0
    if( xLabeledSeq.is())
679
0
        aResult = getLabelForLabeledDataSequence( xLabeledSeq );
680
0
    else
681
0
    {
682
        // special case: labeled data series with only a label and no values may
683
        // serve as label
684
0
        xLabeledSeq.set( lcl_findLSequenceWithOnlyLabel( getDataSequences() ));
685
0
        if( xLabeledSeq.is())
686
0
        {
687
0
            Reference< chart2::data::XDataSequence > xSeq( xLabeledSeq->getLabel());
688
0
            if( xSeq.is())
689
0
                aResult = lcl_getDataSequenceLabel( xSeq );
690
0
        }
691
0
    }
692
693
0
    return aResult;
694
0
}
695
696
static bool lcl_SequenceHasUnhiddenData( const uno::Reference< chart2::data::XDataSequence >& xDataSequence )
697
0
{
698
0
    if (!xDataSequence.is())
699
0
        return false;
700
0
    uno::Reference< beans::XPropertySet > xProp( xDataSequence, uno::UNO_QUERY );
701
0
    if( xProp.is() )
702
0
    {
703
0
        uno::Sequence< sal_Int32 > aHiddenValues;
704
0
        try
705
0
        {
706
0
            xProp->getPropertyValue( u"HiddenValues"_ustr ) >>= aHiddenValues;
707
0
            if( !aHiddenValues.hasElements() )
708
0
                return true;
709
0
        }
710
0
        catch( const uno::Exception& )
711
0
        {
712
0
            return true;
713
0
        }
714
0
    }
715
0
    return xDataSequence->getData().hasElements();
716
0
}
717
718
bool DataSeries::hasUnhiddenData()
719
0
{
720
0
    MutexGuard aGuard( m_aMutex );
721
722
0
    for(uno::Reference< chart2::data::XLabeledDataSequence > const & rDataSequence : m_aDataSequences)
723
0
    {
724
0
        if( !rDataSequence.is() )
725
0
            continue;
726
0
        if( lcl_SequenceHasUnhiddenData( rDataSequence->getValues() ) )
727
0
            return true;
728
0
    }
729
0
    return false;
730
0
}
731
732
bool DataSeries::hasPointOwnColor(
733
        sal_Int32 nPointIndex
734
        , const css::uno::Reference< css::beans::XPropertySet >& xDataPointProperties //may be NULL this is just for performance
735
         )
736
0
{
737
0
    if( hasPointOwnProperties( nPointIndex ))
738
0
    {
739
0
        uno::Reference< beans::XPropertyState > xPointState( xDataPointProperties, uno::UNO_QUERY );
740
0
        if( !xPointState.is() )
741
0
        {
742
0
            xPointState.set( getDataPointByIndex( nPointIndex ), uno::UNO_QUERY );
743
0
        }
744
0
        if( !xPointState.is() )
745
0
            return false;
746
747
0
        return (xPointState->getPropertyState( u"Color"_ustr) != beans::PropertyState_DEFAULT_VALUE );
748
0
    }
749
750
0
    return false;
751
0
}
752
753
bool DataSeries::hasPointOwnProperties( sal_Int32 nPointIndex )
754
0
{
755
0
    MutexGuard aGuard( m_aMutex );
756
0
    return m_aAttributedDataPoints.find(nPointIndex) != m_aAttributedDataPoints.end();
757
0
}
758
759
sal_Int32 DataSeries::getAttachedAxisIndex()
760
0
{
761
0
    sal_Int32 nRet = 0;
762
0
    try
763
0
    {
764
0
        getFastPropertyValue( ::chart::DataSeriesProperties::PROP_DATASERIES_ATTACHED_AXIS_INDEX ) >>= nRet;
765
0
    }
766
0
    catch( const uno::Exception & )
767
0
    {
768
0
        DBG_UNHANDLED_EXCEPTION("chart2");
769
0
    }
770
0
    return nRet;
771
0
}
772
773
void DataSeries::switchSymbolsOnOrOff( bool bSymbolsOn, sal_Int32 nSeriesIndex )
774
0
{
775
0
    css::chart2::Symbol aSymbProp;
776
0
    if( getPropertyValue( u"Symbol"_ustr) >>= aSymbProp )
777
0
    {
778
0
        if( !bSymbolsOn )
779
0
            aSymbProp.Style = chart2::SymbolStyle_NONE;
780
0
        else if( aSymbProp.Style == chart2::SymbolStyle_NONE )
781
0
        {
782
0
            aSymbProp.Style = chart2::SymbolStyle_STANDARD;
783
0
            aSymbProp.StandardSymbol = nSeriesIndex;
784
0
        }
785
0
        setPropertyValue( u"Symbol"_ustr, uno::Any( aSymbProp ));
786
0
    }
787
    //todo: check attributed data points
788
0
}
789
790
void DataSeries::switchLinesOnOrOff( bool bLinesOn )
791
0
{
792
0
    if( bLinesOn )
793
0
    {
794
        // keep line-styles that are not NONE
795
0
        css::drawing::LineStyle eLineStyle;
796
0
        if( (getPropertyValue( u"LineStyle"_ustr) >>= eLineStyle ) &&
797
0
            eLineStyle == drawing::LineStyle_NONE )
798
0
        {
799
0
            setPropertyValue( u"LineStyle"_ustr, uno::Any( drawing::LineStyle_SOLID ) );
800
0
        }
801
0
    }
802
0
    else
803
0
        setPropertyValue( u"LineStyle"_ustr, uno::Any( drawing::LineStyle_NONE ) );
804
0
}
805
806
void DataSeries::makeLinesThickOrThin( bool bThick )
807
0
{
808
0
    sal_Int32 nNewValue = bThick ? 80 : 0;
809
0
    sal_Int32 nOldValue = 0;
810
0
    if( (getPropertyValue( u"LineWidth"_ustr) >>= nOldValue ) &&
811
0
        nOldValue != nNewValue )
812
0
    {
813
0
        if( !(bThick && nOldValue>0))
814
0
            setPropertyValue( u"LineWidth"_ustr, uno::Any( nNewValue ) );
815
0
    }
816
0
}
817
818
void DataSeries::setPropertyAlsoToAllAttributedDataPoints( const OUString& rPropertyName, const uno::Any& rPropertyValue )
819
0
{
820
0
    setPropertyValue( rPropertyName, rPropertyValue );
821
822
0
    std::vector<sal_Int32> aAttributedDataPointIndexList;
823
0
    {
824
0
        MutexGuard aGuard( m_aMutex );
825
0
        aAttributedDataPointIndexList.reserve(m_aAttributedDataPoints.size());
826
0
        for (const auto & rPair : m_aAttributedDataPoints)
827
0
            aAttributedDataPointIndexList.push_back(rPair.first);
828
0
    }
829
830
0
    for(sal_Int32 nIdx : aAttributedDataPointIndexList)
831
0
    {
832
0
        Reference< beans::XPropertySet > xPointProp( getDataPointByIndex(nIdx) );
833
0
        if(!xPointProp.is())
834
0
            continue;
835
0
        xPointProp->setPropertyValue( rPropertyName, rPropertyValue );
836
0
        if( rPropertyName == "LabelPlacement" )
837
0
        {
838
0
            xPointProp->setPropertyValue(u"CustomLabelPosition"_ustr, uno::Any());
839
0
            xPointProp->setPropertyValue(u"CustomLabelSize"_ustr, uno::Any());
840
0
        }
841
0
    }
842
0
}
843
844
bool DataSeries::hasAttributedDataPointDifferentValue(
845
                                              const OUString& rPropertyName, const uno::Any& rPropertyValue )
846
0
{
847
0
    std::vector<sal_Int32> aAttributedDataPointIndexList;
848
0
    {
849
0
        MutexGuard aGuard( m_aMutex );
850
0
        aAttributedDataPointIndexList.reserve(m_aAttributedDataPoints.size());
851
0
        for (const auto & rPair : m_aAttributedDataPoints)
852
0
            aAttributedDataPointIndexList.push_back(rPair.first);
853
0
    }
854
855
0
    for(sal_Int32 nIdx : aAttributedDataPointIndexList)
856
0
    {
857
0
        Reference< beans::XPropertySet > xPointProp( getDataPointByIndex(nIdx) );
858
0
        if(!xPointProp.is())
859
0
            continue;
860
0
        uno::Any aPointValue( xPointProp->getPropertyValue( rPropertyName ) );
861
0
        if( rPropertyValue != aPointValue )
862
0
            return true;
863
0
    }
864
865
0
    return false;
866
0
}
867
868
bool DataSeries::hasDataLabelsAtSeries()
869
0
{
870
0
    bool bRet = false;
871
0
    try
872
0
    {
873
0
        chart2::DataPointLabel aLabel;
874
0
        if( getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel )
875
0
            bRet = aLabel.ShowNumber || aLabel.ShowNumberInPercent || aLabel.ShowCategoryName
876
0
                   || aLabel.ShowSeriesName;
877
0
    }
878
0
    catch(const uno::Exception &)
879
0
    {
880
0
        TOOLS_WARN_EXCEPTION("chart2", "" );
881
0
    }
882
0
    return bRet;
883
0
}
884
885
bool DataSeries::hasDataLabelsAtPoints()
886
0
{
887
0
    bool bRet = false;
888
0
    try
889
0
    {
890
0
        std::vector<sal_Int32> aAttributedDataPointIndexList;
891
0
        {
892
0
            MutexGuard aGuard( m_aMutex );
893
0
            aAttributedDataPointIndexList.reserve(m_aAttributedDataPoints.size());
894
0
            for (const auto & rPair : m_aAttributedDataPoints)
895
0
                aAttributedDataPointIndexList.push_back(rPair.first);
896
0
        }
897
0
        for(sal_Int32 nIdx : aAttributedDataPointIndexList)
898
0
        {
899
0
            Reference< beans::XPropertySet > xPointProp( getDataPointByIndex(nIdx) );
900
0
            if( xPointProp.is() )
901
0
            {
902
0
                chart2::DataPointLabel aLabel;
903
0
                if( xPointProp->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel )
904
0
                    bRet = aLabel.ShowNumber || aLabel.ShowNumberInPercent
905
0
                           || aLabel.ShowCategoryName || aLabel.ShowCustomLabel
906
0
                           || aLabel.ShowSeriesName;
907
0
                if( bRet )
908
0
                    break;
909
0
            }
910
0
        }
911
0
    }
912
0
    catch(const uno::Exception &)
913
0
    {
914
0
        TOOLS_WARN_EXCEPTION("chart2", "" );
915
0
    }
916
0
    return bRet;
917
0
}
918
919
bool DataSeries::hasDataLabelAtPoint( sal_Int32 nPointIndex )
920
0
{
921
0
    bool bRet = false;
922
0
    try
923
0
    {
924
0
        Reference< beans::XPropertySet > xProp;
925
0
        bool bFound = false;
926
0
        {
927
0
            MutexGuard aGuard( m_aMutex );
928
0
            bFound = m_aAttributedDataPoints.find(nPointIndex) != m_aAttributedDataPoints.end();
929
0
        }
930
0
        if (bFound)
931
0
            xProp = getDataPointByIndex(nPointIndex);
932
0
        else
933
0
            xProp = this;
934
0
        if( xProp.is() )
935
0
        {
936
0
            chart2::DataPointLabel aLabel;
937
0
            if( xProp->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel )
938
0
                bRet = aLabel.ShowNumber || aLabel.ShowNumberInPercent
939
0
                       || aLabel.ShowCategoryName || aLabel.ShowCustomLabel
940
0
                       || aLabel.ShowSeriesName;
941
0
        }
942
0
    }
943
0
    catch(const uno::Exception &)
944
0
    {
945
0
        TOOLS_WARN_EXCEPTION("chart2", "" );
946
0
    }
947
0
    return bRet;
948
0
}
949
950
void DataSeries::insertDataLabelsToSeriesAndAllPoints()
951
0
{
952
0
    impl_insertOrDeleteDataLabelsToSeriesAndAllPoints( true /*bInsert*/ );
953
0
}
954
955
void DataSeries::deleteDataLabelsFromSeriesAndAllPoints()
956
0
{
957
0
    impl_insertOrDeleteDataLabelsToSeriesAndAllPoints( false /*bInsert*/ );
958
0
}
959
960
void DataSeries::impl_insertOrDeleteDataLabelsToSeriesAndAllPoints( bool bInsert )
961
0
{
962
0
    try
963
0
    {
964
0
        chart2::DataPointLabel aLabelAtSeries;
965
0
        getPropertyValue(CHART_UNONAME_LABEL) >>= aLabelAtSeries;
966
0
        aLabelAtSeries.ShowNumber = bInsert;
967
0
        if( !bInsert )
968
0
        {
969
0
            aLabelAtSeries.ShowNumberInPercent = false;
970
0
            aLabelAtSeries.ShowCategoryName = false;
971
0
        }
972
0
        setPropertyValue(CHART_UNONAME_LABEL, uno::Any(aLabelAtSeries));
973
0
        std::vector<sal_Int32> aAttributedDataPointIndexList;
974
0
        {
975
0
            MutexGuard aGuard( m_aMutex );
976
0
            aAttributedDataPointIndexList.reserve(m_aAttributedDataPoints.size());
977
0
            for (const auto & rPair : m_aAttributedDataPoints)
978
0
                aAttributedDataPointIndexList.push_back(rPair.first);
979
0
        }
980
0
        for(sal_Int32 nIdx : aAttributedDataPointIndexList)
981
0
        {
982
0
            Reference< beans::XPropertySet > xPointProp( getDataPointByIndex(nIdx) );
983
0
            if( xPointProp.is() )
984
0
            {
985
0
                chart2::DataPointLabel aLabel;
986
0
                xPointProp->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel;
987
0
                aLabel.ShowNumber = bInsert;
988
0
                if( !bInsert )
989
0
                {
990
0
                    aLabel.ShowNumberInPercent = false;
991
0
                    aLabel.ShowCategoryName = false;
992
0
                    aLabel.ShowCustomLabel = false;
993
0
                    aLabel.ShowSeriesName = false;
994
0
                }
995
0
                xPointProp->setPropertyValue(CHART_UNONAME_LABEL, uno::Any(aLabel));
996
0
                xPointProp->setPropertyValue(CHART_UNONAME_CUSTOM_LABEL_FIELDS, uno::Any());
997
0
            }
998
0
        }
999
0
    }
1000
0
    catch(const uno::Exception &)
1001
0
    {
1002
0
        TOOLS_WARN_EXCEPTION("chart2", "" );
1003
0
    }
1004
0
}
1005
1006
sal_Int32 DataSeries::getExplicitNumberFormatKeyForDataLabel()
1007
0
{
1008
0
    sal_Int32 nFormat = 0;
1009
0
    try
1010
0
    {
1011
0
        bool bLinkToSource = true;
1012
0
        getPropertyValue(CHART_UNONAME_LINK_TO_SRC_NUMFMT) >>= bLinkToSource;
1013
0
        getPropertyValue(CHART_UNONAME_NUMFMT) >>= nFormat;
1014
1015
0
        if (bLinkToSource)
1016
0
        {
1017
0
            MutexGuard aGuard( m_aMutex );
1018
1019
0
            if (!m_aDataSequences.empty())
1020
0
            {
1021
0
                Reference<chart2::data::XLabeledDataSequence> xLabeledSeq(m_aDataSequences[0]);
1022
0
                if( xLabeledSeq.is() )
1023
0
                {
1024
0
                    Reference< chart2::data::XDataSequence > xSeq( xLabeledSeq->getValues());
1025
0
                    if( xSeq.is() )
1026
0
                    {
1027
0
                        nFormat = xSeq->getNumberFormatKeyByIndex( -1 );
1028
0
                    }
1029
0
                }
1030
0
            }
1031
0
        }
1032
0
    }
1033
0
    catch (const beans::UnknownPropertyException&)
1034
0
    {
1035
0
    }
1036
1037
0
    if (nFormat < 0)
1038
0
        nFormat = 0;
1039
0
    return nFormat;
1040
1041
0
}
1042
1043
}  // namespace chart
1044
1045
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
1046
com_sun_star_comp_chart_DataSeries_get_implementation(css::uno::XComponentContext *,
1047
        css::uno::Sequence<css::uno::Any> const &)
1048
0
{
1049
0
    return cppu::acquire(new ::chart::DataSeries );
1050
0
}
1051
1052
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */