summaryrefslogtreecommitdiffstats
path: root/chart2/source/controller/main/ObjectHierarchy.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /chart2/source/controller/main/ObjectHierarchy.cxx
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'chart2/source/controller/main/ObjectHierarchy.cxx')
-rw-r--r--chart2/source/controller/main/ObjectHierarchy.cxx718
1 files changed, 718 insertions, 0 deletions
diff --git a/chart2/source/controller/main/ObjectHierarchy.cxx b/chart2/source/controller/main/ObjectHierarchy.cxx
new file mode 100644
index 000000000..5a04b7ae4
--- /dev/null
+++ b/chart2/source/controller/main/ObjectHierarchy.cxx
@@ -0,0 +1,718 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ObjectHierarchy.hxx>
+#include <ObjectIdentifier.hxx>
+#include <ChartModelHelper.hxx>
+#include <DiagramHelper.hxx>
+#include <Diagram.hxx>
+#include <RegressionCurveHelper.hxx>
+#include <Axis.hxx>
+#include <AxisHelper.hxx>
+#include <chartview/ExplicitValueProvider.hxx>
+#include <ChartType.hxx>
+#include <ChartTypeHelper.hxx>
+#include <ChartModel.hxx>
+#include <DataSeries.hxx>
+#include <DataSeriesHelper.hxx>
+#include <LegendHelper.hxx>
+#include <chartview/DrawModelWrapper.hxx>
+#include <unonames.hxx>
+#include <BaseCoordinateSystem.hxx>
+
+#include <map>
+#include <algorithm>
+#include <cstddef>
+
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/chart/ErrorBarStyle.hpp>
+
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/awt/Key.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+
+namespace
+{
+
+void lcl_getChildOIDs(
+ ::chart::ObjectHierarchy::tChildContainer& rOutChildren,
+ const Reference< container::XIndexAccess >& xShapes )
+{
+ if( !xShapes.is())
+ return;
+
+ sal_Int32 nCount = xShapes->getCount();
+ for( sal_Int32 i=0; i<nCount; ++i)
+ {
+ Reference< beans::XPropertySet > xShapeProp( xShapes->getByIndex( i ), uno::UNO_QUERY );
+ if( xShapeProp.is())
+ {
+ Reference< beans::XPropertySetInfo > xInfo( xShapeProp->getPropertySetInfo());
+ OUString aName;
+ if( xInfo.is() &&
+ xInfo->hasPropertyByName( "Name") &&
+ (xShapeProp->getPropertyValue( "Name") >>= aName ) &&
+ !aName.isEmpty() &&
+ ::chart::ObjectIdentifier::isCID( aName ))
+ {
+ rOutChildren.emplace_back( aName );
+ }
+ Reference< container::XIndexAccess > xNewShapes( xShapeProp, uno::UNO_QUERY );
+ if( xNewShapes.is())
+ lcl_getChildOIDs( rOutChildren, xNewShapes );
+ }
+ }
+}
+
+void lcl_addAxisTitle( const rtl::Reference< ::chart::Axis >& xAxis, ::chart::ObjectHierarchy::tChildContainer& rContainer, const rtl::Reference<::chart::ChartModel>& xChartModel )
+{
+ if( xAxis.is())
+ {
+ Reference< XTitle > xAxisTitle( xAxis->getTitleObject());
+ if( xAxisTitle.is())
+ rContainer.emplace_back( ::chart::ObjectIdentifier::createClassifiedIdentifierForObject( xAxisTitle, xChartModel ) );
+ }
+}
+
+} // anonymous namespace
+
+namespace chart
+{
+
+void ObjectHierarchy::createTree( const rtl::Reference<::chart::ChartModel>& xChartDocument )
+{
+ m_aChildMap = tChildMap();//clear tree
+
+ if( !xChartDocument.is() )
+ return;
+
+ //@todo: change ObjectIdentifier to take an XChartDocument rather than XModel
+ rtl::Reference< Diagram > xDiagram = ChartModelHelper::findDiagram( xChartDocument );
+ ObjectIdentifier aDiaOID;
+ if( xDiagram.is() )
+ aDiaOID = ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForObject( static_cast<cppu::OWeakObject*>(xDiagram.get()), xChartDocument ) );
+ ObjectHierarchy::tChildContainer aTopLevelContainer;
+
+ // First Level
+
+ // Chart Area
+ if( m_bOrderingForElementSelector )
+ {
+ aTopLevelContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, u"" ) );
+ if( xDiagram.is() )
+ {
+ aTopLevelContainer.push_back( aDiaOID );
+ createWallAndFloor( aTopLevelContainer, xDiagram );
+ createLegendTree( aTopLevelContainer, xChartDocument, xDiagram );
+ }
+ }
+
+ // Main Title
+ Reference< XTitle > xMainTitle( xChartDocument->getTitleObject());
+ if( xMainTitle.is())
+ aTopLevelContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForObject( xMainTitle, xChartDocument ) );
+
+ if( xDiagram.is())
+ {
+ // Sub Title. Note: This is interpreted of being top level
+ Reference< XTitle > xSubTitle( xDiagram->getTitleObject());
+ if( xSubTitle.is())
+ aTopLevelContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForObject( xSubTitle, xChartDocument ) );
+
+ if( !m_bOrderingForElementSelector )
+ {
+ // Axis Titles. Note: These are interpreted of being top level
+ const std::vector< rtl::Reference< Axis > > aAxes = AxisHelper::getAllAxesOfDiagram( xDiagram );
+ for( rtl::Reference< Axis > const & axis : aAxes )
+ lcl_addAxisTitle( axis, aTopLevelContainer, xChartDocument );
+
+ // Diagram
+ aTopLevelContainer.push_back( aDiaOID );
+ }
+
+ if( m_bFlattenDiagram )
+ createDiagramTree( aTopLevelContainer, xChartDocument, xDiagram );
+ else
+ {
+ ObjectHierarchy::tChildContainer aSubContainer;
+ createDiagramTree( aSubContainer, xChartDocument, xDiagram );
+ if( !aSubContainer.empty() )
+ m_aChildMap[ aDiaOID ] = aSubContainer;
+ }
+
+ if( !m_bOrderingForElementSelector )
+ createLegendTree( aTopLevelContainer, xChartDocument, xDiagram );
+ }
+
+ // #i12587# support for shapes in chart
+ if ( !m_bOrderingForElementSelector )
+ {
+ createAdditionalShapesTree( aTopLevelContainer );
+ }
+
+ // Chart Area
+ if( !m_bOrderingForElementSelector )
+ aTopLevelContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, u"" ) );
+
+ if( ! aTopLevelContainer.empty())
+ m_aChildMap[ ObjectHierarchy::getRootNodeOID() ] = aTopLevelContainer;
+}
+
+void ObjectHierarchy::createLegendTree(
+ ObjectHierarchy::tChildContainer & rContainer,
+ const rtl::Reference<::chart::ChartModel> & xChartDoc,
+ const rtl::Reference< Diagram > & xDiagram )
+{
+ if( !(xDiagram.is() && LegendHelper::hasLegend( xDiagram )) )
+ return;
+
+ ObjectIdentifier aLegendOID( ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForObject( xDiagram->getLegend(), xChartDoc ) ) );
+ rContainer.push_back( aLegendOID );
+
+ // iterate over child shapes of legend and search for matching CIDs
+ if( m_pExplicitValueProvider )
+ {
+ rtl::Reference< SvxShapeGroupAnyD > xLegendShapeContainer =
+ dynamic_cast<SvxShapeGroupAnyD*>(
+ m_pExplicitValueProvider->getShapeForCID( aLegendOID.getObjectCID() ).get() );
+ ObjectHierarchy::tChildContainer aLegendEntryOIDs;
+ lcl_getChildOIDs( aLegendEntryOIDs, xLegendShapeContainer );
+
+ m_aChildMap[ aLegendOID ] = aLegendEntryOIDs;
+ }
+}
+
+void ObjectHierarchy::createAxesTree(
+ ObjectHierarchy::tChildContainer & rContainer,
+ const rtl::Reference<::chart::ChartModel> & xChartDoc,
+ const rtl::Reference< Diagram > & xDiagram )
+{
+ sal_Int32 nDimensionCount = DiagramHelper::getDimension( xDiagram );
+ rtl::Reference< ChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
+ bool bSupportsAxesGrids = ChartTypeHelper::isSupportingMainAxis( xChartType, nDimensionCount, 0 );
+ if( !bSupportsAxesGrids )
+ return;
+
+ std::vector< rtl::Reference< Axis > > aAxes = AxisHelper::getAllAxesOfDiagram( xDiagram, /* bOnlyVisible = */ true );
+ if( !m_bOrderingForElementSelector )
+ {
+ for (const auto & rAxis : std::as_const(aAxes))
+ rContainer.push_back( ObjectIdentifier::createClassifiedIdentifierForObject( rAxis, xChartDoc ) );
+ }
+
+ // get all axes, also invisible ones
+ aAxes = AxisHelper::getAllAxesOfDiagram( xDiagram );
+ // Grids
+ for( rtl::Reference< Axis > const & xAxis : aAxes )
+ {
+ if(!xAxis.is())
+ continue;
+
+ sal_Int32 nCooSysIndex = 0;
+ sal_Int32 nDimensionIndex = 0;
+ sal_Int32 nAxisIndex = 0;
+ AxisHelper::getIndicesForAxis( xAxis, xDiagram, nCooSysIndex, nDimensionIndex, nAxisIndex );
+ if( nAxisIndex>0 && !ChartTypeHelper::isSupportingSecondaryAxis( xChartType, nDimensionCount ) )
+ continue;
+
+ if( m_bOrderingForElementSelector )
+ {
+ // axis
+ if( AxisHelper::isAxisVisible( xAxis ) )
+ rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForObject( xAxis, xChartDoc ) );
+
+ // axis title
+ lcl_addAxisTitle( xAxis, rContainer, xChartDoc );
+ }
+
+ Reference< beans::XPropertySet > xGridProperties( xAxis->getGridProperties() );
+ if( AxisHelper::isGridVisible( xGridProperties ) )
+ {
+ //main grid
+ rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForGrid( xAxis, xChartDoc ) );
+ }
+
+ Sequence< Reference< beans::XPropertySet > > aSubGrids( xAxis->getSubGridProperties() );
+ sal_Int32 nSubGrid = 0;
+ for( nSubGrid = 0; nSubGrid < aSubGrids.getLength(); ++nSubGrid )
+ {
+ Reference< beans::XPropertySet > xSubGridProperties( aSubGrids[nSubGrid] );
+ if( AxisHelper::isGridVisible( xSubGridProperties ) )
+ {
+ //sub grid
+ rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForGrid( xAxis, xChartDoc, nSubGrid ) );
+ }
+ }
+ }
+}
+
+void ObjectHierarchy::createWallAndFloor(
+ ObjectHierarchy::tChildContainer & rContainer,
+ const rtl::Reference< Diagram > & xDiagram )
+{
+ sal_Int32 nDimensionCount = DiagramHelper::getDimension( xDiagram );
+ bool bIsThreeD = ( nDimensionCount == 3 );
+ bool bHasWall = DiagramHelper::isSupportingFloorAndWall( xDiagram );
+ if( bHasWall && bIsThreeD )
+ {
+ rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL, u"" ) );
+
+ Reference< beans::XPropertySet > xFloor( xDiagram->getFloor());
+ if( xFloor.is())
+ rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_FLOOR, u"" ) );
+ }
+
+}
+
+void ObjectHierarchy::createDiagramTree(
+ ObjectHierarchy::tChildContainer & rContainer,
+ const rtl::Reference<::chart::ChartModel> & xChartDoc,
+ const rtl::Reference< Diagram > & xDiagram )
+{
+ if( !m_bOrderingForElementSelector )
+ {
+ createDataSeriesTree( rContainer, xDiagram );
+ createAxesTree( rContainer, xChartDoc, xDiagram );
+ createWallAndFloor( rContainer, xDiagram );
+ }
+ else
+ {
+ createAxesTree( rContainer, xChartDoc, xDiagram );
+ createDataSeriesTree( rContainer, xDiagram );
+ }
+}
+
+void ObjectHierarchy::createDataSeriesTree(
+ ObjectHierarchy::tChildContainer & rOutDiagramSubContainer,
+ const rtl::Reference< Diagram > & xDiagram )
+{
+ try
+ {
+ sal_Int32 nDimensionCount = DiagramHelper::getDimension( xDiagram );
+ std::vector< rtl::Reference< BaseCoordinateSystem > > aCooSysSeq(
+ xDiagram->getBaseCoordinateSystems());
+ for( std::size_t nCooSysIdx=0; nCooSysIdx<aCooSysSeq.size(); ++nCooSysIdx )
+ {
+ std::vector< rtl::Reference< ChartType > > aChartTypeSeq( aCooSysSeq[nCooSysIdx]->getChartTypes2());
+ for( std::size_t nCTIdx=0; nCTIdx<aChartTypeSeq.size(); ++nCTIdx )
+ {
+ rtl::Reference< ChartType > xChartType( aChartTypeSeq[nCTIdx] );
+ std::vector< rtl::Reference< DataSeries > > aSeriesSeq( xChartType->getDataSeries2() );
+ const sal_Int32 nNumberOfSeries =
+ ChartTypeHelper::getNumberOfDisplayedSeries( xChartType, aSeriesSeq.size());
+
+ for( sal_Int32 nSeriesIdx=0; nSeriesIdx<nNumberOfSeries; ++nSeriesIdx )
+ {
+ OUString aSeriesParticle(
+ ObjectIdentifier::createParticleForSeries(
+ 0, nCooSysIdx, nCTIdx, nSeriesIdx ));
+ ObjectIdentifier aSeriesOID(
+ ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForParticle( aSeriesParticle ) ) );
+ rOutDiagramSubContainer.push_back( aSeriesOID );
+
+ ObjectHierarchy::tChildContainer aSeriesSubContainer;
+
+ rtl::Reference< DataSeries > const & xSeries = aSeriesSeq[nSeriesIdx];
+
+ // data labels
+ if( DataSeriesHelper::hasDataLabelsAtSeries( xSeries ) )
+ {
+ OUString aChildParticle( ObjectIdentifier::getStringForType( OBJECTTYPE_DATA_LABELS ) + "=" );
+ aSeriesSubContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForParticles( aSeriesParticle, aChildParticle ) );
+ }
+
+ // Statistics
+ if( ChartTypeHelper::isSupportingStatisticProperties( xChartType, nDimensionCount ) )
+ {
+ Sequence< Reference< chart2::XRegressionCurve > > aCurves( xSeries->getRegressionCurves());
+ for( sal_Int32 nCurveIdx=0; nCurveIdx<aCurves.getLength(); ++nCurveIdx )
+ {
+ bool bIsAverageLine = RegressionCurveHelper::isMeanValueLine( aCurves[nCurveIdx] );
+ aSeriesSubContainer.emplace_back( ObjectIdentifier::createDataCurveCID( aSeriesParticle, nCurveIdx, bIsAverageLine ) );
+ if( RegressionCurveHelper::hasEquation( aCurves[nCurveIdx] ) )
+ {
+ aSeriesSubContainer.emplace_back( ObjectIdentifier::createDataCurveEquationCID( aSeriesParticle, nCurveIdx ) );
+ }
+ }
+ Reference< beans::XPropertySet > xErrorBarProp;
+ if( (xSeries->getPropertyValue( CHART_UNONAME_ERRORBAR_Y) >>= xErrorBarProp) &&
+ xErrorBarProp.is())
+ {
+ sal_Int32 nStyle = css::chart::ErrorBarStyle::NONE;
+ if( ( xErrorBarProp->getPropertyValue( "ErrorBarStyle") >>= nStyle ) &&
+ ( nStyle != css::chart::ErrorBarStyle::NONE ) )
+ {
+ aSeriesSubContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierWithParent(
+ OBJECTTYPE_DATA_ERRORS_Y, u"", aSeriesParticle ) );
+ }
+ }
+
+ if( (xSeries->getPropertyValue(CHART_UNONAME_ERRORBAR_X) >>= xErrorBarProp) &&
+ xErrorBarProp.is())
+ {
+ sal_Int32 nStyle = css::chart::ErrorBarStyle::NONE;
+ if( ( xErrorBarProp->getPropertyValue( "ErrorBarStyle") >>= nStyle ) &&
+ ( nStyle != css::chart::ErrorBarStyle::NONE ) )
+ {
+ aSeriesSubContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierWithParent(
+ OBJECTTYPE_DATA_ERRORS_X, u"", aSeriesParticle ) );
+ }
+ }
+ }
+
+ // Data Points
+ // iterate over child shapes of legend and search for matching CIDs
+ if( m_pExplicitValueProvider )
+ {
+ rtl::Reference< SvxShapeGroupAnyD > xSeriesShapeContainer =
+ dynamic_cast<SvxShapeGroupAnyD*>(
+ m_pExplicitValueProvider->getShapeForCID( aSeriesOID.getObjectCID() ).get() );
+ lcl_getChildOIDs( aSeriesSubContainer, xSeriesShapeContainer );
+ }
+
+ if( ! aSeriesSubContainer.empty())
+ m_aChildMap[ aSeriesOID ] = aSeriesSubContainer;
+ }
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+void ObjectHierarchy::createAdditionalShapesTree( ObjectHierarchy::tChildContainer& rContainer )
+{
+ try
+ {
+ if ( m_pExplicitValueProvider )
+ {
+ rtl::Reference<SvxDrawPage> xDrawPage( m_pExplicitValueProvider->getDrawModelWrapper()->getMainDrawPage() );
+ Reference< drawing::XShapes > xChartRoot( DrawModelWrapper::getChartRootShape( xDrawPage ) );
+ sal_Int32 nCount = xDrawPage->getCount();
+ for ( sal_Int32 i = 0; i < nCount; ++i )
+ {
+ Reference< drawing::XShape > xShape;
+ if ( xDrawPage->getByIndex( i ) >>= xShape )
+ {
+ if ( xShape.is() && xShape != xChartRoot )
+ {
+ rContainer.emplace_back( xShape );
+ }
+ }
+ }
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+bool ObjectHierarchy::hasChildren( const ObjectIdentifier& rParent ) const
+{
+ if ( rParent.isValid() )
+ {
+ tChildMap::const_iterator aIt( m_aChildMap.find( rParent ));
+ if( aIt != m_aChildMap.end())
+ return ! (aIt->second.empty());
+ }
+ return false;
+}
+
+const ObjectHierarchy::tChildContainer & ObjectHierarchy::getChildren( const ObjectIdentifier& rParent ) const
+{
+ if ( rParent.isValid() )
+ {
+ tChildMap::const_iterator aIt( m_aChildMap.find( rParent ));
+ if( aIt != m_aChildMap.end())
+ return aIt->second;
+ }
+ static const ObjectHierarchy::tChildContainer EMPTY;
+ return EMPTY;
+}
+
+const ObjectHierarchy::tChildContainer & ObjectHierarchy::getSiblings( const ObjectIdentifier& rNode ) const
+{
+ if ( rNode.isValid() && !ObjectHierarchy::isRootNode( rNode ) )
+ {
+ for (auto const& child : m_aChildMap)
+ {
+ ObjectHierarchy::tChildContainer::const_iterator aElemIt(
+ std::find( child.second.begin(), child.second.end(), rNode ));
+ if( aElemIt != child.second.end())
+ return child.second;
+ }
+ }
+ static const ObjectHierarchy::tChildContainer EMPTY;
+ return EMPTY;
+}
+
+ObjectIdentifier ObjectHierarchy::getParentImpl(
+ const ObjectIdentifier & rParentOID,
+ const ObjectIdentifier & rOID ) const
+{
+ // search children
+ ObjectHierarchy::tChildContainer aChildren( getChildren( rParentOID ));
+ ObjectHierarchy::tChildContainer::const_iterator aIt(
+ std::find( aChildren.begin(), aChildren.end(), rOID ));
+ // recursion end
+ if( aIt != aChildren.end())
+ return rParentOID;
+
+ for (auto const& child : aChildren)
+ {
+ // recursion
+ ObjectIdentifier aTempParent( getParentImpl( child, rOID ));
+ if ( aTempParent.isValid() )
+ {
+ // exit on success
+ return aTempParent;
+ }
+ }
+
+ // exit on fail
+ return ObjectIdentifier();
+}
+
+ObjectIdentifier ObjectHierarchy::getParent(
+ const ObjectIdentifier & rOID ) const
+{
+ return getParentImpl( ObjectHierarchy::getRootNodeOID(), rOID );
+}
+
+ObjectHierarchy::ObjectHierarchy(
+ const rtl::Reference<::chart::ChartModel> & xChartDocument,
+ ExplicitValueProvider * pExplicitValueProvider /* = 0 */,
+ bool bFlattenDiagram /* = false */,
+ bool bOrderingForElementSelector /* = false */) :
+ m_pExplicitValueProvider( pExplicitValueProvider ),
+ m_bFlattenDiagram( bFlattenDiagram ),
+ m_bOrderingForElementSelector( bOrderingForElementSelector )
+{
+ createTree( xChartDocument );
+ // don't remember this helper to avoid access after lifetime
+ m_pExplicitValueProvider = nullptr;
+}
+
+ObjectHierarchy::~ObjectHierarchy()
+{}
+
+ObjectIdentifier ObjectHierarchy::getRootNodeOID()
+{
+ return ObjectIdentifier( "ROOT" );
+}
+
+bool ObjectHierarchy::isRootNode( const ObjectIdentifier& rOID )
+{
+ return ( rOID == ObjectHierarchy::getRootNodeOID() );
+}
+
+const ObjectHierarchy::tChildContainer & ObjectHierarchy::getTopLevelChildren() const
+{
+ return getChildren( ObjectHierarchy::getRootNodeOID());
+}
+
+sal_Int32 ObjectHierarchy::getIndexInParent(
+ const ObjectIdentifier& rNode ) const
+{
+ ObjectIdentifier aParentOID( getParent( rNode ));
+ const tChildContainer & aChildren( getChildren( aParentOID ) );
+ sal_Int32 nIndex = 0;
+ for (auto const& child : aChildren)
+ {
+ if ( child == rNode )
+ return nIndex;
+ ++nIndex;
+ }
+ return -1;
+}
+
+ObjectKeyNavigation::ObjectKeyNavigation(
+ const ObjectIdentifier & rCurrentOID,
+ const rtl::Reference<::chart::ChartModel> & xChartDocument,
+ ExplicitValueProvider * pExplicitValueProvider /* = 0 */ ) :
+ m_aCurrentOID( rCurrentOID ),
+ m_xChartDocument( xChartDocument ),
+ m_pExplicitValueProvider( pExplicitValueProvider )
+{
+ if ( !m_aCurrentOID.isValid() )
+ {
+ setCurrentSelection( ObjectHierarchy::getRootNodeOID() );
+ }
+}
+
+bool ObjectKeyNavigation::handleKeyEvent(
+ const awt::KeyEvent & rEvent )
+{
+ bool bResult = false;
+
+ switch( rEvent.KeyCode )
+ {
+ case awt::Key::TAB:
+ if( rEvent.Modifiers & awt::KeyModifier::SHIFT )
+ bResult = previous();
+ else
+ bResult = next();
+ break;
+ case awt::Key::HOME:
+ bResult = first();
+ break;
+ case awt::Key::END:
+ bResult = last();
+ break;
+ case awt::Key::F3:
+ if( rEvent.Modifiers & awt::KeyModifier::SHIFT )
+ bResult = up();
+ else
+ bResult = down();
+ break;
+ case awt::Key::ESCAPE:
+ setCurrentSelection( ObjectIdentifier() );
+ bResult = true;
+ break;
+ default:
+ bResult = false;
+ break;
+ }
+ return bResult;
+}
+
+void ObjectKeyNavigation::setCurrentSelection( const ObjectIdentifier& rOID )
+{
+ m_aCurrentOID = rOID;
+}
+
+bool ObjectKeyNavigation::first()
+{
+ ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
+ ObjectHierarchy::tChildContainer aSiblings( aHierarchy.getSiblings( getCurrentSelection() ) );
+ bool bResult = !aSiblings.empty();
+ if( bResult )
+ setCurrentSelection( aSiblings.front());
+ else
+ bResult = veryFirst();
+ return bResult;
+}
+
+bool ObjectKeyNavigation::last()
+{
+ ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
+ ObjectHierarchy::tChildContainer aSiblings( aHierarchy.getSiblings( getCurrentSelection() ) );
+ bool bResult = !aSiblings.empty();
+ if( bResult )
+ setCurrentSelection( aSiblings.back());
+ else
+ bResult = veryLast();
+ return bResult;
+}
+
+bool ObjectKeyNavigation::next()
+{
+ ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
+ ObjectHierarchy::tChildContainer aSiblings( aHierarchy.getSiblings( getCurrentSelection() ) );
+ bool bResult = !aSiblings.empty();
+ if( bResult )
+ {
+ ObjectHierarchy::tChildContainer::const_iterator aIt(
+ std::find( aSiblings.begin(), aSiblings.end(), getCurrentSelection()));
+ assert(aIt != aSiblings.end());
+ if( ++aIt == aSiblings.end())
+ aIt = aSiblings.begin();
+ setCurrentSelection( *aIt );
+ }
+ else
+ bResult = veryFirst();
+
+ return bResult;
+}
+
+bool ObjectKeyNavigation::previous()
+{
+ ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
+ ObjectHierarchy::tChildContainer aSiblings( aHierarchy.getSiblings( getCurrentSelection()));
+ bool bResult = !aSiblings.empty();
+ if( bResult )
+ {
+ ObjectHierarchy::tChildContainer::const_iterator aIt(
+ std::find( aSiblings.begin(), aSiblings.end(), getCurrentSelection()));
+ OSL_ASSERT( aIt != aSiblings.end());
+ if( aIt == aSiblings.begin())
+ aIt = aSiblings.end();
+ --aIt;
+ setCurrentSelection( *aIt );
+ }
+ else
+ bResult = veryLast();
+ return bResult;
+}
+
+bool ObjectKeyNavigation::up()
+{
+ ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
+ bool bResult = !ObjectHierarchy::isRootNode( getCurrentSelection());
+ if( bResult )
+ setCurrentSelection( aHierarchy.getParent( getCurrentSelection()));
+ return bResult;
+}
+
+bool ObjectKeyNavigation::down()
+{
+ ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
+ bool bResult = aHierarchy.hasChildren( getCurrentSelection());
+ if( bResult )
+ {
+ ObjectHierarchy::tChildContainer aChildren = aHierarchy.getChildren( getCurrentSelection());
+ OSL_ASSERT( !aChildren.empty());
+ setCurrentSelection( aChildren.front());
+ }
+ return bResult;
+}
+
+bool ObjectKeyNavigation::veryFirst()
+{
+ ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
+ ObjectHierarchy::tChildContainer aChildren( aHierarchy.getTopLevelChildren());
+ bool bResult = !aChildren.empty();
+ if( bResult )
+ setCurrentSelection( aChildren.front());
+ return bResult;
+}
+
+bool ObjectKeyNavigation::veryLast()
+{
+ ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider );
+ ObjectHierarchy::tChildContainer aChildren( aHierarchy.getTopLevelChildren());
+ bool bResult = !aChildren.empty();
+ if( bResult )
+ setCurrentSelection( aChildren.back());
+ return bResult;
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */