Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/chart2/source/controller/main/ObjectHierarchy.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 <ObjectHierarchy.hxx>
21
#include <ObjectIdentifier.hxx>
22
#include <ChartView.hxx>
23
#include <Diagram.hxx>
24
#include <RegressionCurveHelper.hxx>
25
#include <RegressionCurveModel.hxx>
26
#include <Axis.hxx>
27
#include <AxisHelper.hxx>
28
#include <ChartType.hxx>
29
#include <ChartTypeHelper.hxx>
30
#include <ChartModel.hxx>
31
#include <DataSeries.hxx>
32
#include <GridProperties.hxx>
33
#include <LegendHelper.hxx>
34
#include <chartview/DrawModelWrapper.hxx>
35
#include <unonames.hxx>
36
#include <BaseCoordinateSystem.hxx>
37
38
#include <map>
39
#include <algorithm>
40
#include <cstddef>
41
42
#include <com/sun/star/drawing/XShapes.hpp>
43
#include <com/sun/star/chart/ErrorBarStyle.hpp>
44
45
#include <com/sun/star/container/XIndexAccess.hpp>
46
#include <com/sun/star/awt/Key.hpp>
47
#include <com/sun/star/awt/KeyModifier.hpp>
48
#include <utility>
49
#include <comphelper/diagnose_ex.hxx>
50
51
using namespace ::com::sun::star;
52
using namespace ::com::sun::star::chart2;
53
54
using ::com::sun::star::uno::Reference;
55
56
namespace
57
{
58
59
void lcl_getChildOIDs(
60
    ::chart::ObjectHierarchy::tChildContainer& rOutChildren,
61
    const Reference< container::XIndexAccess >& xShapes )
62
0
{
63
0
    if( !xShapes.is())
64
0
        return;
65
66
0
    sal_Int32 nCount = xShapes->getCount();
67
0
    for( sal_Int32 i=0; i<nCount; ++i)
68
0
    {
69
0
        Reference< beans::XPropertySet > xShapeProp( xShapes->getByIndex( i ), uno::UNO_QUERY );
70
0
        if( xShapeProp.is())
71
0
        {
72
0
            Reference< beans::XPropertySetInfo > xInfo( xShapeProp->getPropertySetInfo());
73
0
            OUString aName;
74
0
            if( xInfo.is() &&
75
0
                xInfo->hasPropertyByName( u"Name"_ustr) &&
76
0
                (xShapeProp->getPropertyValue( u"Name"_ustr) >>= aName ) &&
77
0
                !aName.isEmpty() &&
78
0
                ::chart::ObjectIdentifier::isCID( aName ))
79
0
            {
80
0
                rOutChildren.emplace_back( aName );
81
0
            }
82
0
            Reference< container::XIndexAccess > xNewShapes( xShapeProp, uno::UNO_QUERY );
83
0
            if( xNewShapes.is())
84
0
                lcl_getChildOIDs( rOutChildren, xNewShapes );
85
0
        }
86
0
    }
87
0
}
88
89
void lcl_addAxisTitle( const rtl::Reference< ::chart::Axis >& xAxis, ::chart::ObjectHierarchy::tChildContainer& rContainer, const rtl::Reference<::chart::ChartModel>& xChartModel )
90
0
{
91
0
    if( xAxis.is())
92
0
    {
93
0
        Reference< XTitle > xAxisTitle( xAxis->getTitleObject());
94
0
        if( xAxisTitle.is())
95
0
            rContainer.emplace_back( ::chart::ObjectIdentifier::createClassifiedIdentifierForObject( xAxisTitle, xChartModel ) );
96
0
    }
97
0
}
98
99
} // anonymous namespace
100
101
namespace chart
102
{
103
104
void ObjectHierarchy::createTree( const rtl::Reference<::chart::ChartModel>& xChartDocument )
105
0
{
106
0
    m_aChildMap.clear();
107
108
0
    if( !xChartDocument.is() )
109
0
        return;
110
111
    //@todo: change ObjectIdentifier to take an XChartDocument rather than XModel
112
0
    rtl::Reference< Diagram > xDiagram = xChartDocument->getFirstChartDiagram();
113
0
    ObjectIdentifier aDiaOID;
114
0
    if( xDiagram.is() )
115
0
        aDiaOID = ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForObject( static_cast<cppu::OWeakObject*>(xDiagram.get()), xChartDocument ) );
116
0
    tChildContainer aTopLevelContainer;
117
118
    // First Level
119
120
    // Chart Area
121
0
    if( m_bOrderingForElementSelector )
122
0
    {
123
0
        aTopLevelContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, u"" ) );
124
0
        if( xDiagram.is() )
125
0
        {
126
0
            aTopLevelContainer.push_back( aDiaOID );
127
0
            createWallAndFloor( aTopLevelContainer, xDiagram );
128
0
            createLegendTree( aTopLevelContainer, xChartDocument, xDiagram  );
129
0
        }
130
0
    }
131
132
    // Main Title
133
0
    Reference< XTitle > xMainTitle( xChartDocument->getTitleObject());
134
0
    if( xMainTitle.is())
135
0
        aTopLevelContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForObject( xMainTitle, xChartDocument ) );
136
137
0
    if( xDiagram.is())
138
0
    {
139
        // Sub Title.  Note: This is interpreted of being top level
140
0
        Reference< XTitle > xSubTitle( xDiagram->getTitleObject());
141
0
        if( xSubTitle.is())
142
0
            aTopLevelContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForObject( xSubTitle, xChartDocument ) );
143
144
0
        if( !m_bOrderingForElementSelector )
145
0
        {
146
            // Axis Titles. Note: These are interpreted of being top level
147
0
            const std::vector< rtl::Reference< Axis > > aAxes = AxisHelper::getAllAxesOfDiagram( xDiagram );
148
0
            for( rtl::Reference< Axis > const & axis : aAxes )
149
0
                lcl_addAxisTitle( axis, aTopLevelContainer, xChartDocument );
150
151
            // Diagram
152
0
            aTopLevelContainer.push_back( aDiaOID );
153
0
        }
154
155
0
        if( m_bFlattenDiagram )
156
0
            createDiagramTree( aTopLevelContainer, xChartDocument, xDiagram );
157
0
        else
158
0
        {
159
0
            tChildContainer aSubContainer;
160
0
            createDiagramTree( aSubContainer, xChartDocument, xDiagram );
161
0
            if( !aSubContainer.empty() )
162
0
                m_aChildMap[ aDiaOID ] = std::move(aSubContainer);
163
0
        }
164
165
0
        if( !m_bOrderingForElementSelector )
166
0
            createLegendTree( aTopLevelContainer, xChartDocument, xDiagram  );
167
0
    }
168
169
    // #i12587# support for shapes in chart
170
0
    if ( !m_bOrderingForElementSelector )
171
0
    {
172
0
        createAdditionalShapesTree( aTopLevelContainer );
173
0
    }
174
175
    // Chart Area
176
0
    if( !m_bOrderingForElementSelector )
177
0
        aTopLevelContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, u"" ) );
178
179
0
    if( ! aTopLevelContainer.empty())
180
0
        m_aChildMap[ObjectHierarchy::getRootNodeOID()] = std::move(aTopLevelContainer);
181
0
}
182
183
void ObjectHierarchy::createLegendTree(
184
    tChildContainer & rContainer,
185
    const rtl::Reference<::chart::ChartModel> & xChartDoc,
186
    const rtl::Reference< Diagram > & xDiagram  )
187
0
{
188
0
    if( !(xDiagram.is() && LegendHelper::hasLegend( xDiagram )) )
189
0
        return;
190
191
0
    ObjectIdentifier aLegendOID( ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForObject( xDiagram->getLegend(), xChartDoc ) ) );
192
0
    rContainer.push_back( aLegendOID );
193
194
    // iterate over child shapes of legend and search for matching CIDs
195
0
    if( m_pExplicitValueProvider )
196
0
    {
197
0
        rtl::Reference< SvxShapeGroupAnyD > xLegendShapeContainer =
198
0
            dynamic_cast<SvxShapeGroupAnyD*>(
199
0
                m_pExplicitValueProvider->getShapeForCID( aLegendOID.getObjectCID() ).get() );
200
0
        tChildContainer aLegendEntryOIDs;
201
0
        lcl_getChildOIDs( aLegendEntryOIDs, xLegendShapeContainer );
202
203
0
        m_aChildMap[aLegendOID] = std::move(aLegendEntryOIDs);
204
0
    }
205
0
}
206
207
void ObjectHierarchy::createAxesTree(
208
    tChildContainer & rContainer,
209
    const rtl::Reference<::chart::ChartModel> & xChartDoc,
210
    const rtl::Reference< Diagram > & xDiagram  )
211
0
{
212
0
    sal_Int32 nDimensionCount = xDiagram->getDimension();
213
0
    rtl::Reference< ChartType > xChartType( xDiagram->getChartTypeByIndex( 0 ) );
214
0
    bool bSupportsAxesGrids = true;
215
0
    if (xChartType.is())
216
0
        bSupportsAxesGrids = xChartType->isSupportingMainAxis(nDimensionCount, 0);
217
218
0
    if (!bSupportsAxesGrids)
219
0
        return;
220
221
    // Data Table
222
0
    uno::Reference<chart2::XDataTable> xDataTable = xDiagram->getDataTable();
223
0
    if (xDataTable.is())
224
0
    {
225
0
        rContainer.push_back(ObjectIdentifier::createClassifiedIdentifierForObject(xDataTable, xChartDoc));
226
0
    }
227
228
    // Axes
229
0
    std::vector< rtl::Reference< Axis > > aAxes = AxisHelper::getAllAxesOfDiagram( xDiagram, /* bOnlyVisible = */ true );
230
0
    if( !m_bOrderingForElementSelector )
231
0
    {
232
0
        for (const auto& rAxis : aAxes)
233
0
            rContainer.push_back( ObjectIdentifier::createClassifiedIdentifierForObject( rAxis, xChartDoc ) );
234
0
    }
235
236
    // get all axes, also invisible ones
237
0
    aAxes = AxisHelper::getAllAxesOfDiagram( xDiagram );
238
    // Grids
239
0
    for( rtl::Reference< Axis > const & xAxis : aAxes )
240
0
    {
241
0
        if(!xAxis.is())
242
0
            continue;
243
244
0
        sal_Int32 nCooSysIndex = 0;
245
0
        sal_Int32 nDimensionIndex = 0;
246
0
        sal_Int32 nAxisIndex = 0;
247
0
        AxisHelper::getIndicesForAxis( xAxis, xDiagram, nCooSysIndex, nDimensionIndex, nAxisIndex );
248
0
        if (nAxisIndex > 0 && !(xChartType.is() ? xChartType->isSupportingSecondaryAxis(nDimensionCount) : true))
249
0
            continue;
250
251
0
        if( m_bOrderingForElementSelector )
252
0
        {
253
            // axis
254
0
            if( AxisHelper::isAxisVisible( xAxis ) )
255
0
                rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForObject( xAxis, xChartDoc ) );
256
257
            // axis title
258
0
            lcl_addAxisTitle( xAxis, rContainer, xChartDoc );
259
0
        }
260
261
0
        rtl::Reference< ::chart::GridProperties > xGridProperties( xAxis->getGridProperties2() );
262
0
        if( AxisHelper::isGridVisible( xGridProperties ) )
263
0
        {
264
            //main grid
265
0
            rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForGrid( xAxis, xChartDoc ) );
266
0
        }
267
268
0
        std::vector< rtl::Reference< ::chart::GridProperties > > aSubGrids( xAxis->getSubGridProperties2() );
269
0
        for( size_t nSubGrid = 0; nSubGrid < aSubGrids.size(); ++nSubGrid )
270
0
        {
271
0
            if( AxisHelper::isGridVisible( aSubGrids[nSubGrid] ) )
272
0
            {
273
                //sub grid
274
0
                rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForGrid( xAxis, xChartDoc, nSubGrid ) );
275
0
            }
276
0
        }
277
0
    }
278
0
}
279
280
void ObjectHierarchy::createWallAndFloor(
281
    tChildContainer & rContainer,
282
    const rtl::Reference< Diagram > & xDiagram )
283
0
{
284
0
    sal_Int32 nDimensionCount = xDiagram->getDimension();
285
0
    bool bIsThreeD = ( nDimensionCount == 3 );
286
0
    bool bHasWall = xDiagram->isSupportingFloorAndWall();
287
0
    if( bHasWall && bIsThreeD )
288
0
    {
289
0
        rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL, u"" ) );
290
291
0
        Reference< beans::XPropertySet > xFloor( xDiagram->getFloor());
292
0
        if( xFloor.is())
293
0
            rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_FLOOR, u"" ) );
294
0
    }
295
296
0
}
297
298
void ObjectHierarchy::createDiagramTree(
299
    tChildContainer & rContainer,
300
    const rtl::Reference<::chart::ChartModel> & xChartDoc,
301
    const rtl::Reference< Diagram > & xDiagram )
302
0
{
303
0
    if( !m_bOrderingForElementSelector )
304
0
    {
305
0
        createDataSeriesTree( rContainer, xDiagram );
306
0
        createAxesTree( rContainer, xChartDoc, xDiagram  );
307
0
        createWallAndFloor( rContainer, xDiagram );
308
0
    }
309
0
    else
310
0
    {
311
0
        createAxesTree( rContainer, xChartDoc, xDiagram  );
312
0
        createDataSeriesTree( rContainer, xDiagram );
313
0
    }
314
0
}
315
316
void ObjectHierarchy::createDataSeriesTree(
317
    tChildContainer & rOutDiagramSubContainer,
318
    const rtl::Reference< Diagram > & xDiagram )
319
0
{
320
0
    try
321
0
    {
322
0
        sal_Int32 nDimensionCount = xDiagram->getDimension();
323
0
        std::vector< rtl::Reference< BaseCoordinateSystem > > aCooSysSeq(
324
0
            xDiagram->getBaseCoordinateSystems());
325
0
        for( std::size_t nCooSysIdx=0; nCooSysIdx<aCooSysSeq.size(); ++nCooSysIdx )
326
0
        {
327
0
            std::vector< rtl::Reference< ChartType > > aChartTypeSeq( aCooSysSeq[nCooSysIdx]->getChartTypes2());
328
0
            for( std::size_t nCTIdx=0; nCTIdx<aChartTypeSeq.size(); ++nCTIdx )
329
0
            {
330
0
                const rtl::Reference< ChartType >& xChartType( aChartTypeSeq[nCTIdx] );
331
0
                std::vector< rtl::Reference< DataSeries > > aSeriesSeq( xChartType->getDataSeries2() );
332
0
                const sal_Int32 nNumberOfSeries =
333
0
                    ChartTypeHelper::getNumberOfDisplayedSeries( xChartType, aSeriesSeq.size());
334
335
0
                for( sal_Int32 nSeriesIdx=0; nSeriesIdx<nNumberOfSeries; ++nSeriesIdx )
336
0
                {
337
0
                    OUString aSeriesParticle(
338
0
                        ObjectIdentifier::createParticleForSeries(
339
0
                            0, nCooSysIdx, nCTIdx, nSeriesIdx ));
340
0
                    ObjectIdentifier aSeriesOID(
341
0
                        ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForParticle( aSeriesParticle ) ) );
342
0
                    rOutDiagramSubContainer.push_back( aSeriesOID );
343
344
0
                    tChildContainer aSeriesSubContainer;
345
346
0
                    rtl::Reference< DataSeries > const & xSeries = aSeriesSeq[nSeriesIdx];
347
348
                    // data labels
349
0
                    if( xSeries->hasDataLabelsAtSeries() )
350
0
                    {
351
0
                        OUString aChildParticle( ObjectIdentifier::getStringForType( OBJECTTYPE_DATA_LABELS ) + "=" );
352
0
                        aSeriesSubContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForParticles( aSeriesParticle, aChildParticle ) );
353
0
                    }
354
355
                    // Statistics
356
0
                    if (xChartType.is() ? xChartType->isSupportingStatisticProperties(nDimensionCount) : true)
357
0
                    {
358
0
                        const std::vector< rtl::Reference< RegressionCurveModel > > & rCurves( xSeries->getRegressionCurves2());
359
0
                        for( size_t nCurveIdx=0; nCurveIdx<rCurves.size(); ++nCurveIdx )
360
0
                        {
361
0
                            bool bIsAverageLine = RegressionCurveHelper::isMeanValueLine( rCurves[nCurveIdx] );
362
0
                            aSeriesSubContainer.emplace_back( ObjectIdentifier::createDataCurveCID( aSeriesParticle, nCurveIdx, bIsAverageLine ) );
363
0
                            if( RegressionCurveHelper::hasEquation( rCurves[nCurveIdx] ) )
364
0
                            {
365
0
                                aSeriesSubContainer.emplace_back( ObjectIdentifier::createDataCurveEquationCID( aSeriesParticle, nCurveIdx ) );
366
0
                            }
367
0
                        }
368
0
                        Reference< beans::XPropertySet > xErrorBarProp;
369
0
                        if( (xSeries->getPropertyValue( CHART_UNONAME_ERRORBAR_Y) >>= xErrorBarProp) &&
370
0
                            xErrorBarProp.is())
371
0
                        {
372
0
                            sal_Int32 nStyle = css::chart::ErrorBarStyle::NONE;
373
0
                            if( ( xErrorBarProp->getPropertyValue( u"ErrorBarStyle"_ustr) >>= nStyle ) &&
374
0
                                ( nStyle != css::chart::ErrorBarStyle::NONE ) )
375
0
                            {
376
0
                                aSeriesSubContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierWithParent(
377
0
                                        OBJECTTYPE_DATA_ERRORS_Y, u"", aSeriesParticle ) );
378
0
                            }
379
0
                        }
380
381
0
                        if( (xSeries->getPropertyValue(CHART_UNONAME_ERRORBAR_X) >>= xErrorBarProp) &&
382
0
                            xErrorBarProp.is())
383
0
                        {
384
0
                            sal_Int32 nStyle = css::chart::ErrorBarStyle::NONE;
385
0
                            if( ( xErrorBarProp->getPropertyValue( u"ErrorBarStyle"_ustr) >>= nStyle ) &&
386
0
                                ( nStyle != css::chart::ErrorBarStyle::NONE ) )
387
0
                            {
388
0
                                aSeriesSubContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierWithParent(
389
0
                                        OBJECTTYPE_DATA_ERRORS_X, u"", aSeriesParticle ) );
390
0
                            }
391
0
                        }
392
0
                    }
393
394
                    // Data Points
395
                    // iterate over child shapes of legend and search for matching CIDs
396
0
                    if( m_pExplicitValueProvider )
397
0
                    {
398
0
                        rtl::Reference< SvxShapeGroupAnyD > xSeriesShapeContainer =
399
0
                            dynamic_cast<SvxShapeGroupAnyD*>(
400
0
                            m_pExplicitValueProvider->getShapeForCID( aSeriesOID.getObjectCID() ).get() );
401
0
                        lcl_getChildOIDs( aSeriesSubContainer, xSeriesShapeContainer );
402
0
                    }
403
404
0
                    if( ! aSeriesSubContainer.empty())
405
0
                        m_aChildMap[ aSeriesOID ] = std::move(aSeriesSubContainer);
406
0
                }
407
0
            }
408
0
        }
409
0
    }
410
0
    catch( const uno::Exception & )
411
0
    {
412
0
        DBG_UNHANDLED_EXCEPTION("chart2");
413
0
    }
414
0
}
415
416
void ObjectHierarchy::createAdditionalShapesTree(tChildContainer& rContainer)
417
0
{
418
0
    try
419
0
    {
420
0
        if ( m_pExplicitValueProvider )
421
0
        {
422
0
            rtl::Reference<SvxDrawPage> xDrawPage( m_pExplicitValueProvider->getDrawModelWrapper()->getMainDrawPage() );
423
0
            Reference< drawing::XShapes > xChartRoot( DrawModelWrapper::getChartRootShape( xDrawPage ) );
424
0
            sal_Int32 nCount = xDrawPage->getCount();
425
0
            for ( sal_Int32 i = 0; i < nCount; ++i )
426
0
            {
427
0
                Reference< drawing::XShape > xShape;
428
0
                if ( xDrawPage->getByIndex( i ) >>= xShape )
429
0
                {
430
0
                    if ( xShape.is() && xShape != xChartRoot )
431
0
                    {
432
0
                        rContainer.emplace_back( xShape );
433
0
                    }
434
0
                }
435
0
            }
436
0
        }
437
0
    }
438
0
    catch ( const uno::Exception& )
439
0
    {
440
0
        DBG_UNHANDLED_EXCEPTION("chart2");
441
0
    }
442
0
}
443
444
bool ObjectHierarchy::hasChildren( const ObjectIdentifier& rParent ) const
445
0
{
446
0
    if ( rParent.isValid() )
447
0
    {
448
0
        tChildMap::const_iterator aIt( m_aChildMap.find( rParent ));
449
0
        if( aIt != m_aChildMap.end())
450
0
            return ! (aIt->second.empty());
451
0
    }
452
0
    return false;
453
0
}
454
455
const ObjectHierarchy::tChildContainer & ObjectHierarchy::getChildren( const ObjectIdentifier& rParent ) const
456
0
{
457
0
    if ( rParent.isValid() )
458
0
    {
459
0
        tChildMap::const_iterator aIt( m_aChildMap.find( rParent ));
460
0
        if( aIt != m_aChildMap.end())
461
0
            return aIt->second;
462
0
    }
463
0
    static const tChildContainer EMPTY;
464
0
    return EMPTY;
465
0
}
466
467
const ObjectHierarchy::tChildContainer & ObjectHierarchy::getSiblings( const ObjectIdentifier& rNode ) const
468
0
{
469
0
    if ( rNode.isValid() && !ObjectHierarchy::isRootNode( rNode ) )
470
0
    {
471
0
        for (auto const& child : m_aChildMap)
472
0
        {
473
0
            tChildContainer::const_iterator aElemIt(
474
0
                std::find( child.second.begin(), child.second.end(), rNode ));
475
0
            if( aElemIt != child.second.end())
476
0
                return child.second;
477
0
        }
478
0
    }
479
0
    static const tChildContainer EMPTY;
480
0
    return EMPTY;
481
0
}
482
483
ObjectIdentifier ObjectHierarchy::getParentImpl(
484
    const ObjectIdentifier & rParentOID,
485
    const ObjectIdentifier & rOID ) const
486
0
{
487
    // search children
488
0
    tChildContainer aChildren( getChildren( rParentOID ));
489
0
    tChildContainer::const_iterator aIt(
490
0
        std::find( aChildren.begin(), aChildren.end(), rOID ));
491
    // recursion end
492
0
    if( aIt != aChildren.end())
493
0
        return rParentOID;
494
495
0
    for (auto const& child : aChildren)
496
0
    {
497
        // recursion
498
0
        ObjectIdentifier aTempParent( getParentImpl( child, rOID ));
499
0
        if ( aTempParent.isValid() )
500
0
        {
501
            // exit on success
502
0
            return aTempParent;
503
0
        }
504
0
    }
505
506
    // exit on fail
507
0
    return ObjectIdentifier();
508
0
}
509
510
ObjectIdentifier ObjectHierarchy::getParent(
511
    const ObjectIdentifier & rOID ) const
512
0
{
513
0
    return getParentImpl( ObjectHierarchy::getRootNodeOID(), rOID );
514
0
}
515
516
ObjectHierarchy::ObjectHierarchy(
517
    const rtl::Reference<::chart::ChartModel> & xChartDocument,
518
    ChartView * pExplicitValueProvider /* = 0 */,
519
    bool bFlattenDiagram /* = false */,
520
    bool bOrderingForElementSelector /* = false */) :
521
0
        m_pExplicitValueProvider( pExplicitValueProvider ),
522
0
        m_bFlattenDiagram( bFlattenDiagram ),
523
0
        m_bOrderingForElementSelector( bOrderingForElementSelector )
524
0
{
525
0
    createTree( xChartDocument );
526
    // don't remember this helper to avoid access after lifetime
527
0
    m_pExplicitValueProvider = nullptr;
528
0
}
529
530
ObjectHierarchy::~ObjectHierarchy()
531
0
{}
532
533
ObjectIdentifier ObjectHierarchy::getRootNodeOID()
534
0
{
535
0
    return ObjectIdentifier( u"ROOT"_ustr );
536
0
}
537
538
bool ObjectHierarchy::isRootNode( const ObjectIdentifier& rOID )
539
0
{
540
0
    return ( rOID == ObjectHierarchy::getRootNodeOID() );
541
0
}
542
543
const ObjectHierarchy::tChildContainer & ObjectHierarchy::getTopLevelChildren() const
544
0
{
545
0
    return getChildren( ObjectHierarchy::getRootNodeOID());
546
0
}
547
548
sal_Int32 ObjectHierarchy::getIndexInParent(
549
    const ObjectIdentifier& rNode ) const
550
0
{
551
0
    ObjectIdentifier aParentOID( getParent( rNode ));
552
0
    const tChildContainer & aChildren( getChildren( aParentOID ) );
553
0
    sal_Int32 nIndex = 0;
554
0
    for (auto const& child : aChildren)
555
0
    {
556
0
        if ( child == rNode )
557
0
            return nIndex;
558
0
        ++nIndex;
559
0
    }
560
0
    return -1;
561
0
}
562
563
ObjectKeyNavigation::ObjectKeyNavigation(
564
    ObjectIdentifier aCurrentOID,
565
    rtl::Reference<::chart::ChartModel> xChartDocument,
566
    ChartView * pExplicitValueProvider /* = 0 */ ) :
567
0
        m_aCurrentOID(std::move( aCurrentOID )),
568
0
        m_xChartDocument(std::move( xChartDocument )),
569
0
        m_pExplicitValueProvider( pExplicitValueProvider )
570
0
{
571
0
    if ( !m_aCurrentOID.isValid() )
572
0
    {
573
0
        setCurrentSelection( ObjectHierarchy::getRootNodeOID() );
574
0
    }
575
0
}
576
577
bool ObjectKeyNavigation::handleKeyEvent(
578
    const awt::KeyEvent & rEvent )
579
0
{
580
0
    bool bResult = false;
581
582
0
    switch( rEvent.KeyCode )
583
0
    {
584
0
        case awt::Key::TAB:
585
0
            if( rEvent.Modifiers & awt::KeyModifier::SHIFT )
586
0
                bResult = previous();
587
0
            else
588
0
                bResult = next();
589
0
            break;
590
0
        case awt::Key::HOME:
591
0
            bResult = first();
592
0
            break;
593
0
        case awt::Key::END:
594
0
            bResult = last();
595
0
            break;
596
0
        case awt::Key::F3:
597
0
            if( rEvent.Modifiers & awt::KeyModifier::SHIFT )
598
0
                bResult = up();
599
0
            else
600
0
                bResult = down();
601
0
            break;
602
0
        case awt::Key::ESCAPE:
603
0
            setCurrentSelection( ObjectIdentifier() );
604
0
            bResult = true;
605
0
            break;
606
0
        default:
607
0
            bResult = false;
608
0
            break;
609
0
    }
610
0
    return bResult;
611
0
}
612
613
void ObjectKeyNavigation::setCurrentSelection( const ObjectIdentifier& rOID )
614
0
{
615
0
    m_aCurrentOID = rOID;
616
0
}
617
618
bool ObjectKeyNavigation::first()
619
0
{
620
0
    ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
621
0
    const ObjectHierarchy::tChildContainer& aSiblings( aHierarchy.getSiblings( getCurrentSelection() ) );
622
0
    bool bResult = !aSiblings.empty();
623
0
    if( bResult )
624
0
        setCurrentSelection( aSiblings.front());
625
0
    else
626
0
        bResult = veryFirst();
627
0
    return bResult;
628
0
}
629
630
bool ObjectKeyNavigation::last()
631
0
{
632
0
    ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
633
0
    const ObjectHierarchy::tChildContainer& aSiblings( aHierarchy.getSiblings( getCurrentSelection() ) );
634
0
    bool bResult = !aSiblings.empty();
635
0
    if( bResult )
636
0
        setCurrentSelection( aSiblings.back());
637
0
    else
638
0
        bResult = veryLast();
639
0
    return bResult;
640
0
}
641
642
bool ObjectKeyNavigation::next()
643
0
{
644
0
    ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
645
0
    ObjectHierarchy::tChildContainer aSiblings( aHierarchy.getSiblings( getCurrentSelection() ) );
646
0
    bool bResult = !aSiblings.empty();
647
0
    if( bResult )
648
0
    {
649
0
        ObjectHierarchy::tChildContainer::const_iterator aIt(
650
0
            std::find( aSiblings.begin(), aSiblings.end(), getCurrentSelection()));
651
0
        assert(aIt != aSiblings.end());
652
0
        if( ++aIt == aSiblings.end())
653
0
            aIt = aSiblings.begin();
654
0
        setCurrentSelection( *aIt );
655
0
    }
656
0
    else
657
0
        bResult = veryFirst();
658
659
0
    return bResult;
660
0
}
661
662
bool ObjectKeyNavigation::previous()
663
0
{
664
0
    ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
665
0
    ObjectHierarchy::tChildContainer aSiblings( aHierarchy.getSiblings( getCurrentSelection()));
666
0
    bool bResult = !aSiblings.empty();
667
0
    if( bResult )
668
0
    {
669
0
        ObjectHierarchy::tChildContainer::const_iterator aIt(
670
0
            std::find( aSiblings.begin(), aSiblings.end(), getCurrentSelection()));
671
0
        OSL_ASSERT( aIt != aSiblings.end());
672
0
        if( aIt == aSiblings.begin())
673
0
            aIt = aSiblings.end();
674
0
        --aIt;
675
0
        setCurrentSelection( *aIt );
676
0
    }
677
0
    else
678
0
        bResult = veryLast();
679
0
    return bResult;
680
0
}
681
682
bool ObjectKeyNavigation::up()
683
0
{
684
0
    ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
685
0
    bool bResult = !ObjectHierarchy::isRootNode( getCurrentSelection());
686
0
    if( bResult )
687
0
        setCurrentSelection( aHierarchy.getParent( getCurrentSelection()));
688
0
    return bResult;
689
0
}
690
691
bool ObjectKeyNavigation::down()
692
0
{
693
0
    ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
694
0
    bool bResult = aHierarchy.hasChildren( getCurrentSelection());
695
0
    if( bResult )
696
0
    {
697
0
        const ObjectHierarchy::tChildContainer& aChildren = aHierarchy.getChildren( getCurrentSelection());
698
0
        OSL_ASSERT( !aChildren.empty());
699
0
        setCurrentSelection( aChildren.front());
700
0
    }
701
0
    return bResult;
702
0
}
703
704
bool ObjectKeyNavigation::veryFirst()
705
0
{
706
0
    ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
707
0
    const ObjectHierarchy::tChildContainer& aChildren( aHierarchy.getTopLevelChildren());
708
0
    bool bResult = !aChildren.empty();
709
0
    if( bResult )
710
0
        setCurrentSelection( aChildren.front());
711
0
    return bResult;
712
0
}
713
714
bool ObjectKeyNavigation::veryLast()
715
0
{
716
0
    ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
717
0
    const ObjectHierarchy::tChildContainer& aChildren( aHierarchy.getTopLevelChildren());
718
0
    bool bResult = !aChildren.empty();
719
0
    if( bResult )
720
0
        setCurrentSelection( aChildren.back());
721
0
    return bResult;
722
0
}
723
724
} //  namespace chart
725
726
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */