1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_chart2.hxx"
26 
27 #include "ObjectHierarchy.hxx"
28 #include "ObjectIdentifier.hxx"
29 #include "ChartModelHelper.hxx"
30 #include "DiagramHelper.hxx"
31 #include "RegressionCurveHelper.hxx"
32 #include "AxisHelper.hxx"
33 #include "chartview/ExplicitValueProvider.hxx"
34 #include "macros.hxx"
35 #include "LineProperties.hxx"
36 #include "ChartTypeHelper.hxx"
37 #include "DataSeriesHelper.hxx"
38 #include "LegendHelper.hxx"
39 #include "chartview/DrawModelWrapper.hxx"
40 
41 #include <map>
42 #include <algorithm>
43 
44 #include <com/sun/star/chart2/XTitled.hpp>
45 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
46 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
47 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
48 #include <com/sun/star/chart/ErrorBarStyle.hpp>
49 
50 #include <com/sun/star/container/XIndexAccess.hpp>
51 #include <com/sun/star/awt/Key.hpp>
52 #include <com/sun/star/awt/KeyModifier.hpp>
53 
54 using namespace ::com::sun::star;
55 using namespace ::com::sun::star::chart2;
56 
57 using ::com::sun::star::uno::Reference;
58 using ::com::sun::star::uno::Sequence;
59 using ::rtl::OUString;
60 
61 namespace
62 {
63 
64 struct lcl_ObjectToOID : public ::std::unary_function< Reference< uno::XInterface >, ::chart::ObjectIdentifier >
65 {
lcl_ObjectToOID__anonb45fec9f0111::lcl_ObjectToOID66     explicit lcl_ObjectToOID( const Reference< chart2::XChartDocument > & xChartDoc ) :
67             m_xModel( xChartDoc, uno::UNO_QUERY )
68     {}
69 
operator ()__anonb45fec9f0111::lcl_ObjectToOID70     ::chart::ObjectIdentifier operator() ( const Reference< uno::XInterface > & xObj )
71     {
72         return ::chart::ObjectIdentifier( ::chart::ObjectIdentifier::createClassifiedIdentifierForObject( xObj, m_xModel ) );
73     }
74 
75 private:
76     Reference< frame::XModel > m_xModel;
77 };
78 
lcl_getChildOIDs(::chart::ObjectHierarchy::tChildContainer & rOutChildren,const Reference<container::XIndexAccess> & xShapes)79 void lcl_getChildOIDs(
80     ::chart::ObjectHierarchy::tChildContainer& rOutChildren,
81     const Reference< container::XIndexAccess >& xShapes )
82 {
83     if( xShapes.is())
84     {
85         sal_Int32 nCount = xShapes->getCount();
86         for( sal_Int32 i=0; i<nCount; ++i)
87         {
88             Reference< beans::XPropertySet > xShapeProp( xShapes->getByIndex( i ), uno::UNO_QUERY );
89             if( xShapeProp.is())
90             {
91                 Reference< beans::XPropertySetInfo > xInfo( xShapeProp->getPropertySetInfo());
92                 OUString aName;
93                 if( xInfo.is() &&
94                     xInfo->hasPropertyByName( C2U("Name")) &&
95                     (xShapeProp->getPropertyValue( C2U("Name")) >>= aName ) &&
96                     !aName.isEmpty() &&
97                     ::chart::ObjectIdentifier::isCID( aName ))
98                 {
99                     rOutChildren.push_back( ::chart::ObjectIdentifier( aName ) );
100                 }
101                 Reference< container::XIndexAccess > xNewShapes( xShapeProp, uno::UNO_QUERY );
102                 if( xNewShapes.is())
103                     lcl_getChildOIDs( rOutChildren, xNewShapes );
104             }
105         }
106     }
107 }
108 
lcl_addAxisTitle(const Reference<XAxis> & xAxis,::chart::ObjectHierarchy::tChildContainer & rContainer,const Reference<frame::XModel> & xChartModel)109 void lcl_addAxisTitle( const Reference< XAxis >& xAxis, ::chart::ObjectHierarchy::tChildContainer& rContainer, const Reference< frame::XModel >& xChartModel )
110 {
111     Reference< XTitled > xAxisTitled( xAxis, uno::UNO_QUERY );
112     if( xAxisTitled.is())
113     {
114         Reference< XTitle > xAxisTitle( xAxisTitled->getTitleObject());
115         if( xAxisTitle.is())
116             rContainer.push_back(
117                 ::chart::ObjectIdentifier( ::chart::ObjectIdentifier::createClassifiedIdentifierForObject( xAxisTitle, xChartModel ) ) );
118     }
119 }
120 
121 } // anonymous namespace
122 
123 namespace chart
124 {
125 
126 namespace impl
127 {
128 
129 class ImplObjectHierarchy
130 {
131 public:
132     explicit ImplObjectHierarchy(
133         const Reference< XChartDocument >& xChartDocument,
134         ExplicitValueProvider* pExplicitValueProvider,
135         bool bFlattenDiagram, bool bOrderingForElementSelector );
136 
137     bool                              hasChildren( const ObjectHierarchy::tOID& rParent );
138     ObjectHierarchy::tChildContainer  getChildren( const ObjectHierarchy::tOID& rParent );
139     ObjectHierarchy::tChildContainer  getSiblings( const ObjectHierarchy::tOID& rNode );
140 
141     ObjectHierarchy::tOID             getParent( const ObjectHierarchy::tOID& rOID );
142 
143 private:
144     void createTree( const Reference< XChartDocument > & xChartDocument );
145     void createAxesTree(
146         ObjectHierarchy::tChildContainer & rContainer,
147         const Reference< XChartDocument > & xChartDoc,
148         const Reference< XDiagram > & xDiagram  );
149     void createDiagramTree(
150         ObjectHierarchy::tChildContainer& rContainer,
151         const Reference< XChartDocument >& xChartDoc,
152         const Reference< XDiagram >& xDiagram );
153     void createDataSeriesTree(
154         ObjectHierarchy::tChildContainer & rOutDiagramSubContainer,
155         const Reference< XDiagram > & xDiagram );
156     void createWallAndFloor(
157         ObjectHierarchy::tChildContainer & rContainer,
158         const Reference< XDiagram > & xDiagram );
159     void createLegendTree(
160         ObjectHierarchy::tChildContainer & rContainer,
161         const Reference< XChartDocument > & xChartDoc,
162         const Reference< XDiagram > & xDiagram  );
163     void createAdditionalShapesTree( ObjectHierarchy::tChildContainer& rContainer );
164 
165     ObjectHierarchy::tOID getParentImpl(
166         const ObjectHierarchy::tOID& rParentOID,
167         const ObjectHierarchy::tOID& rOID );
168 
169     typedef ::std::map< ObjectHierarchy::tOID, ObjectHierarchy::tChildContainer >
170         tChildMap;
171     tChildMap m_aChildMap;
172     ExplicitValueProvider* m_pExplicitValueProvider;
173     bool m_bFlattenDiagram;
174     bool m_bOrderingForElementSelector;
175 };
176 
ImplObjectHierarchy(const Reference<XChartDocument> & xChartDocument,ExplicitValueProvider * pExplicitValueProvider,bool bFlattenDiagram,bool bOrderingForElementSelector)177 ImplObjectHierarchy::ImplObjectHierarchy(
178     const Reference< XChartDocument >& xChartDocument,
179     ExplicitValueProvider* pExplicitValueProvider,
180     bool bFlattenDiagram,
181     bool bOrderingForElementSelector ) :
182         m_pExplicitValueProvider( pExplicitValueProvider ),
183         m_bFlattenDiagram( bFlattenDiagram ),
184         m_bOrderingForElementSelector( bOrderingForElementSelector )
185 {
186     createTree( xChartDocument );
187     // don't remember this helper to avoid access after lifetime
188     m_pExplicitValueProvider = 0;
189 }
190 
createTree(const Reference<XChartDocument> & xChartDocument)191 void ImplObjectHierarchy::createTree( const Reference< XChartDocument >& xChartDocument )
192 {
193     m_aChildMap = tChildMap();//clear tree
194 
195     if( !xChartDocument.is() )
196         return;
197 
198     //@todo: change ObjectIdentifier to take an XChartDocument rather than XModel
199     Reference< frame::XModel > xModel( xChartDocument, uno::UNO_QUERY );
200     Reference< XDiagram > xDiagram( ChartModelHelper::findDiagram( xChartDocument ) );
201     ObjectHierarchy::tOID aDiaOID;
202     if( xDiagram.is() )
203         aDiaOID = ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForObject( xDiagram, xModel ) );
204     ObjectHierarchy::tChildContainer aTopLevelContainer;
205 
206     // First Level
207 
208     // Chart Area
209     if( m_bOrderingForElementSelector )
210     {
211         aTopLevelContainer.push_back( ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, OUString() ) ) );
212         if( xDiagram.is() )
213         {
214             aTopLevelContainer.push_back( aDiaOID );
215             createWallAndFloor( aTopLevelContainer, xDiagram );
216             createLegendTree( aTopLevelContainer, xChartDocument, xDiagram  );
217         }
218     }
219 
220     // Main Title
221     Reference< XTitled > xDocTitled( xChartDocument, uno::UNO_QUERY );
222     if( xDocTitled.is())
223     {
224         Reference< XTitle > xMainTitle( xDocTitled->getTitleObject());
225         if( xMainTitle.is())
226             aTopLevelContainer.push_back(
227                 ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForObject( xMainTitle, xModel ) ) );
228     }
229 
230     if( xDiagram.is())
231     {
232         // Sub Title.  Note: This is interpreted of being top level
233         Reference< XTitled > xDiaTitled( xDiagram, uno::UNO_QUERY );
234         if( xDiaTitled.is())
235         {
236             Reference< XTitle > xSubTitle( xDiaTitled->getTitleObject());
237             if( xSubTitle.is())
238                 aTopLevelContainer.push_back(
239                     ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForObject( xSubTitle, xModel ) ) );
240         }
241 
242         if( !m_bOrderingForElementSelector )
243         {
244             // Axis Titles. Note: These are interpreted of being top level
245             Sequence< Reference< XAxis > > aAxes( AxisHelper::getAllAxesOfDiagram( xDiagram ) );
246             for( sal_Int32 i=0; i<aAxes.getLength(); ++i )
247                 lcl_addAxisTitle( aAxes[i], aTopLevelContainer, xModel );
248 
249             // Diagram
250             aTopLevelContainer.push_back( aDiaOID );
251         }
252 
253         if( m_bFlattenDiagram )
254             createDiagramTree( aTopLevelContainer, xChartDocument, xDiagram );
255         else
256         {
257             ObjectHierarchy::tChildContainer aSubContainer;
258             createDiagramTree( aSubContainer, xChartDocument, xDiagram );
259             if( !aSubContainer.empty() )
260                 m_aChildMap[ aDiaOID ] = aSubContainer;
261         }
262 
263         if( !m_bOrderingForElementSelector )
264             createLegendTree( aTopLevelContainer, xChartDocument, xDiagram  );
265     }
266 
267     // #i12587# support for shapes in chart
268     if ( !m_bOrderingForElementSelector )
269     {
270         createAdditionalShapesTree( aTopLevelContainer );
271     }
272 
273     // Chart Area
274     if( !m_bOrderingForElementSelector )
275         aTopLevelContainer.push_back(
276             ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, OUString() ) ) );
277 
278     if( ! aTopLevelContainer.empty())
279         m_aChildMap[ ObjectHierarchy::getRootNodeOID() ] = aTopLevelContainer;
280 }
281 
createLegendTree(ObjectHierarchy::tChildContainer & rContainer,const Reference<XChartDocument> & xChartDoc,const Reference<XDiagram> & xDiagram)282 void ImplObjectHierarchy::createLegendTree(
283     ObjectHierarchy::tChildContainer & rContainer,
284     const Reference< XChartDocument > & xChartDoc,
285     const Reference< XDiagram > & xDiagram  )
286 {
287     if( xDiagram.is() && LegendHelper::hasLegend( xDiagram ) )
288     {
289         ObjectHierarchy::tOID aLegendOID( ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForObject( xDiagram->getLegend(), Reference< frame::XModel >( xChartDoc, uno::UNO_QUERY ) ) ) );
290         rContainer.push_back( aLegendOID );
291 
292         // iterate over child shapes of legend and search for matching CIDs
293         if( m_pExplicitValueProvider )
294         {
295             Reference< container::XIndexAccess > xLegendShapeContainer(
296                 m_pExplicitValueProvider->getShapeForCID( aLegendOID.getObjectCID() ), uno::UNO_QUERY );
297             ObjectHierarchy::tChildContainer aLegendEntryOIDs;
298             lcl_getChildOIDs( aLegendEntryOIDs, xLegendShapeContainer );
299 
300             m_aChildMap[ aLegendOID ] = aLegendEntryOIDs;
301         }
302     }
303 }
304 
createAxesTree(ObjectHierarchy::tChildContainer & rContainer,const Reference<XChartDocument> & xChartDoc,const Reference<XDiagram> & xDiagram)305 void ImplObjectHierarchy::createAxesTree(
306     ObjectHierarchy::tChildContainer & rContainer,
307     const Reference< XChartDocument > & xChartDoc,
308     const Reference< XDiagram > & xDiagram  )
309 {
310     Reference< XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY_THROW );
311     sal_Int32 nDimensionCount = DiagramHelper::getDimension( xDiagram );
312     uno::Reference< chart2::XChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
313     bool bSupportsAxesGrids = ChartTypeHelper::isSupportingMainAxis( xChartType, nDimensionCount, 0 );
314     if( bSupportsAxesGrids )
315     {
316         Sequence< Reference< XAxis > > aAxes( AxisHelper::getAllAxesOfDiagram( xDiagram, /* bOnlyVisible = */ true ) );
317         if( !m_bOrderingForElementSelector )
318             ::std::transform( aAxes.getConstArray(), aAxes.getConstArray() + aAxes.getLength(),
319                           ::std::back_inserter( rContainer ),
320                           lcl_ObjectToOID( xChartDoc ));
321 
322         // get all axes, also invisible ones
323         aAxes = AxisHelper::getAllAxesOfDiagram( xDiagram, /* bOnlyVisible = */ false );
324         // Grids
325         Reference< frame::XModel > xChartModel( xChartDoc, uno::UNO_QUERY );
326         for( sal_Int32 nA=0; nA<aAxes.getLength(); ++nA )
327         {
328             Reference< XAxis > xAxis( aAxes[nA] );
329             if(!xAxis.is())
330                 continue;
331 
332             sal_Int32 nCooSysIndex = 0;
333             sal_Int32 nDimensionIndex = 0;
334             sal_Int32 nAxisIndex = 0;
335             AxisHelper::getIndicesForAxis( xAxis, xDiagram, nCooSysIndex, nDimensionIndex, nAxisIndex );
336             if( nAxisIndex>0 && !ChartTypeHelper::isSupportingSecondaryAxis( xChartType, nDimensionCount, nDimensionIndex ) )
337                 continue;
338 
339             if( m_bOrderingForElementSelector )
340             {
341                 // axis
342                 if( AxisHelper::isAxisVisible( xAxis ) )
343                     rContainer.push_back(
344                         ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForObject( xAxis, xChartModel ) ) );
345 
346                 // axis title
347                 lcl_addAxisTitle( aAxes[nA], rContainer, xChartModel );
348             }
349 
350             Reference< beans::XPropertySet > xGridProperties( xAxis->getGridProperties() );
351             if( AxisHelper::isGridVisible( xGridProperties ) )
352             {
353                 //main grid
354                 rContainer.push_back(
355                     ObjectIdentifier( ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForGrid( xAxis, xChartModel ) ) ) );
356             }
357 
358             Sequence< Reference< beans::XPropertySet > > aSubGrids( xAxis->getSubGridProperties() );;
359             sal_Int32 nSubGrid = 0;
360             for( nSubGrid = 0; nSubGrid < aSubGrids.getLength(); ++nSubGrid )
361             {
362                 Reference< beans::XPropertySet > xSubGridProperties( aSubGrids[nSubGrid] );
363                 if( AxisHelper::isGridVisible( xSubGridProperties ) )
364                 {
365                     //sub grid
366                     rContainer.push_back(
367                         ObjectIdentifier( ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForGrid( xAxis, xChartModel, nSubGrid ) ) ) );
368                 }
369             }
370         }
371 	}
372 }
373 
createWallAndFloor(ObjectHierarchy::tChildContainer & rContainer,const Reference<XDiagram> & xDiagram)374 void ImplObjectHierarchy::createWallAndFloor(
375     ObjectHierarchy::tChildContainer & rContainer,
376     const Reference< XDiagram > & xDiagram )
377 {
378     sal_Int32 nDimensionCount = DiagramHelper::getDimension( xDiagram );
379     bool bIsThreeD = ( nDimensionCount == 3 );
380     bool bHasWall = DiagramHelper::isSupportingFloorAndWall( xDiagram );
381     if( bHasWall && bIsThreeD )
382     {
383         rContainer.push_back(
384             ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL, rtl::OUString() ) ) );
385 
386         Reference< beans::XPropertySet > xFloor( xDiagram->getFloor());
387         if( xFloor.is())
388             rContainer.push_back(
389                 ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_FLOOR, rtl::OUString() ) ) );
390     }
391 
392 }
393 
createDiagramTree(ObjectHierarchy::tChildContainer & rContainer,const Reference<XChartDocument> & xChartDoc,const Reference<XDiagram> & xDiagram)394 void ImplObjectHierarchy::createDiagramTree(
395     ObjectHierarchy::tChildContainer & rContainer,
396     const Reference< XChartDocument > & xChartDoc,
397     const Reference< XDiagram > & xDiagram )
398 {
399     if( !m_bOrderingForElementSelector )
400     {
401         createDataSeriesTree( rContainer, xDiagram );
402         createAxesTree( rContainer, xChartDoc, xDiagram  );
403         createWallAndFloor( rContainer, xDiagram );
404     }
405     else
406     {
407         createAxesTree( rContainer, xChartDoc, xDiagram  );
408         createDataSeriesTree( rContainer, xDiagram );
409     }
410 }
411 
createDataSeriesTree(ObjectHierarchy::tChildContainer & rOutDiagramSubContainer,const Reference<XDiagram> & xDiagram)412 void ImplObjectHierarchy::createDataSeriesTree(
413     ObjectHierarchy::tChildContainer & rOutDiagramSubContainer,
414     const Reference< XDiagram > & xDiagram )
415 {
416     Reference< XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY_THROW );
417 
418     try
419     {
420         sal_Int32 nDiagramIndex = 0;
421         sal_Int32 nDimensionCount = DiagramHelper::getDimension( xDiagram );
422         Sequence< Reference< XCoordinateSystem > > aCooSysSeq(
423             xCooSysCnt->getCoordinateSystems());
424         for( sal_Int32 nCooSysIdx=0; nCooSysIdx<aCooSysSeq.getLength(); ++nCooSysIdx )
425         {
426             Reference< XChartTypeContainer > xCTCnt( aCooSysSeq[nCooSysIdx], uno::UNO_QUERY_THROW );
427             Sequence< Reference< XChartType > > aChartTypeSeq( xCTCnt->getChartTypes());
428             for( sal_Int32 nCTIdx=0; nCTIdx<aChartTypeSeq.getLength(); ++nCTIdx )
429             {
430                 Reference< XChartType > xChartType( aChartTypeSeq[nCTIdx] );
431                 Reference< XDataSeriesContainer > xDSCnt( xChartType, uno::UNO_QUERY_THROW );
432                 Sequence< Reference< XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries() );
433                 const sal_Int32 nNumberOfSeries =
434                     ChartTypeHelper::getNumberOfDisplayedSeries( xChartType, aSeriesSeq.getLength());
435 
436                 for( sal_Int32 nSeriesIdx=0; nSeriesIdx<nNumberOfSeries; ++nSeriesIdx )
437                 {
438                     OUString aSeriesParticle(
439                         ObjectIdentifier::createParticleForSeries(
440                             nDiagramIndex, nCooSysIdx, nCTIdx, nSeriesIdx ));
441                     ObjectHierarchy::tOID aSeriesOID(
442                         ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForParticle( aSeriesParticle ) ) );
443                     rOutDiagramSubContainer.push_back( aSeriesOID );
444 
445                     ObjectHierarchy::tChildContainer aSeriesSubContainer;
446 
447                     Reference< chart2::XDataSeries > xSeries( aSeriesSeq[nSeriesIdx], uno::UNO_QUERY );
448 
449                     // data lablels
450                     if( DataSeriesHelper::hasDataLabelsAtSeries( xSeries ) )
451                     {
452                         rtl::OUString aChildParticle( ObjectIdentifier::getStringForType( OBJECTTYPE_DATA_LABELS ) );
453                         aChildParticle+=(C2U("="));
454                         aSeriesSubContainer.push_back(
455                                     ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForParticles( aSeriesParticle, aChildParticle ) ) );
456                     }
457 
458                     // Statistics
459                     if( ChartTypeHelper::isSupportingStatisticProperties( xChartType, nDimensionCount ) )
460                     {
461                         Reference< chart2::XRegressionCurveContainer > xCurveCnt( xSeries, uno::UNO_QUERY );
462                         if( xCurveCnt.is())
463                         {
464                             Sequence< Reference< chart2::XRegressionCurve > > aCurves( xCurveCnt->getRegressionCurves());
465                             for( sal_Int32 nCurveIdx=0; nCurveIdx<aCurves.getLength(); ++nCurveIdx )
466                             {
467                                 bool bIsAverageLine = RegressionCurveHelper::isMeanValueLine( aCurves[nCurveIdx] );
468                                 aSeriesSubContainer.push_back(
469                                     ObjectIdentifier( ObjectIdentifier::createDataCurveCID( aSeriesParticle, nCurveIdx, bIsAverageLine ) ) );
470                                 if( RegressionCurveHelper::hasEquation( aCurves[nCurveIdx] ) )
471                                 {
472                                     aSeriesSubContainer.push_back(
473                                         ObjectIdentifier( ObjectIdentifier::createDataCurveEquationCID( aSeriesParticle, nCurveIdx ) ) );
474                                 }
475                             }
476                             Reference< beans::XPropertySet > xSeriesProp( xSeries, uno::UNO_QUERY );
477                             Reference< beans::XPropertySet > xErrorBarProp;
478                             if( xSeriesProp.is() &&
479                                 (xSeriesProp->getPropertyValue( C2U("ErrorBarY")) >>= xErrorBarProp) &&
480                                 xErrorBarProp.is())
481                             {
482                                 sal_Int32 nStyle = ::com::sun::star::chart::ErrorBarStyle::NONE;
483                                 if( ( xErrorBarProp->getPropertyValue( C2U("ErrorBarStyle")) >>= nStyle ) &&
484                                     ( nStyle != ::com::sun::star::chart::ErrorBarStyle::NONE ) )
485                                 {
486                                     aSeriesSubContainer.push_back(
487                                         ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierWithParent(
488                                             OBJECTTYPE_DATA_ERRORS, OUString(), aSeriesParticle ) ) );
489                                 }
490                             }
491                         }
492                     }
493 
494                     // Data Points
495                     // iterate over child shapes of legend and search for matching CIDs
496                     if( m_pExplicitValueProvider )
497                     {
498                         Reference< container::XIndexAccess > xSeriesShapeContainer(
499                             m_pExplicitValueProvider->getShapeForCID( aSeriesOID.getObjectCID() ), uno::UNO_QUERY );
500                         lcl_getChildOIDs( aSeriesSubContainer, xSeriesShapeContainer );
501                     }
502 
503                     if( ! aSeriesSubContainer.empty())
504                         m_aChildMap[ aSeriesOID ] = aSeriesSubContainer;
505                 }
506             }
507         }
508     }
509     catch( uno::Exception & ex )
510     {
511         ASSERT_EXCEPTION( ex );
512     }
513 }
514 
createAdditionalShapesTree(ObjectHierarchy::tChildContainer & rContainer)515 void ImplObjectHierarchy::createAdditionalShapesTree( ObjectHierarchy::tChildContainer& rContainer )
516 {
517     try
518     {
519         if ( m_pExplicitValueProvider )
520         {
521             Reference< drawing::XDrawPage > xDrawPage( m_pExplicitValueProvider->getDrawModelWrapper()->getMainDrawPage() );
522             Reference< drawing::XShapes > xDrawPageShapes( xDrawPage, uno::UNO_QUERY_THROW );
523             Reference< drawing::XShapes > xChartRoot( DrawModelWrapper::getChartRootShape( xDrawPage ) );
524             sal_Int32 nCount = xDrawPageShapes->getCount();
525             for ( sal_Int32 i = 0; i < nCount; ++i )
526             {
527                 Reference< drawing::XShape > xShape;
528                 if ( xDrawPageShapes->getByIndex( i ) >>= xShape )
529                 {
530                     if ( xShape.is() && xShape != xChartRoot )
531                     {
532                         rContainer.push_back( ObjectIdentifier( xShape ) );
533                     }
534                 }
535             }
536         }
537     }
538     catch ( uno::Exception& ex )
539     {
540         ASSERT_EXCEPTION( ex );
541     }
542 }
543 
hasChildren(const ObjectHierarchy::tOID & rParent)544 bool ImplObjectHierarchy::hasChildren( const ObjectHierarchy::tOID& rParent )
545 {
546     if ( rParent.isValid() )
547     {
548         tChildMap::const_iterator aIt( m_aChildMap.find( rParent ));
549         if( aIt != m_aChildMap.end())
550             return ! (aIt->second.empty());
551     }
552     return false;
553 }
554 
getChildren(const ObjectHierarchy::tOID & rParent)555 ObjectHierarchy::tChildContainer ImplObjectHierarchy::getChildren( const ObjectHierarchy::tOID& rParent )
556 {
557     if ( rParent.isValid() )
558     {
559         tChildMap::const_iterator aIt( m_aChildMap.find( rParent ));
560         if( aIt != m_aChildMap.end())
561             return aIt->second;
562     }
563     return ObjectHierarchy::tChildContainer();
564 }
565 
getSiblings(const ObjectHierarchy::tOID & rNode)566 ObjectHierarchy::tChildContainer ImplObjectHierarchy::getSiblings( const ObjectHierarchy::tOID& rNode )
567 {
568     if ( rNode.isValid() && !ObjectHierarchy::isRootNode( rNode ) )
569     {
570         for( tChildMap::const_iterator aIt( m_aChildMap.begin());
571              aIt != m_aChildMap.end(); ++aIt )
572         {
573             ObjectHierarchy::tChildContainer::const_iterator aElemIt(
574                 ::std::find( aIt->second.begin(), aIt->second.end(), rNode ));
575             if( aElemIt != aIt->second.end())
576                 return aIt->second;
577         }
578     }
579     return ObjectHierarchy::tChildContainer();
580 }
581 
getParentImpl(const ObjectHierarchy::tOID & rParentOID,const ObjectHierarchy::tOID & rOID)582 ObjectHierarchy::tOID ImplObjectHierarchy::getParentImpl(
583     const ObjectHierarchy::tOID & rParentOID,
584     const ObjectHierarchy::tOID & rOID )
585 {
586     // search children
587     ObjectHierarchy::tChildContainer aChildren( getChildren( rParentOID ));
588     ObjectHierarchy::tChildContainer::const_iterator aIt(
589         ::std::find( aChildren.begin(), aChildren.end(), rOID ));
590     // recursion end
591     if( aIt != aChildren.end())
592         return rParentOID;
593 
594     for( aIt = aChildren.begin(); aIt != aChildren.end(); ++aIt )
595     {
596         // recursion
597         ObjectHierarchy::tOID aTempParent( getParentImpl( *aIt, rOID ));
598         if ( aTempParent.isValid() )
599         {
600             // exit on success
601             return aTempParent;
602         }
603     }
604 
605     // exit on fail
606     return ObjectHierarchy::tOID();
607 }
608 
getParent(const ObjectHierarchy::tOID & rOID)609 ObjectHierarchy::tOID ImplObjectHierarchy::getParent(
610     const ObjectHierarchy::tOID & rOID )
611 {
612     return getParentImpl( ObjectHierarchy::getRootNodeOID(), rOID );
613 }
614 
615 } // namespace impl
616 
617 
ObjectHierarchy(const Reference<XChartDocument> & xChartDocument,ExplicitValueProvider * pExplicitValueProvider,bool bFlattenDiagram,bool bOrderingForElementSelector)618 ObjectHierarchy::ObjectHierarchy(
619     const Reference< XChartDocument > & xChartDocument,
620     ExplicitValueProvider * pExplicitValueProvider /* = 0 */,
621     bool bFlattenDiagram /* = false */,
622     bool bOrderingForElementSelector /* = false */) :
623         m_apImpl( new impl::ImplObjectHierarchy( xChartDocument, pExplicitValueProvider, bFlattenDiagram, bOrderingForElementSelector ))
624 {}
625 
~ObjectHierarchy()626 ObjectHierarchy::~ObjectHierarchy()
627 {}
628 
getRootNodeOID()629 ObjectHierarchy::tOID ObjectHierarchy::getRootNodeOID()
630 {
631     return ObjectIdentifier( C2U( "ROOT" ) );
632 }
633 
isRootNode(const ObjectHierarchy::tOID & rOID)634 bool ObjectHierarchy::isRootNode( const ObjectHierarchy::tOID& rOID )
635 {
636     return ( rOID == ObjectHierarchy::getRootNodeOID() );
637 }
638 
getTopLevelChildren() const639 ObjectHierarchy::tChildContainer ObjectHierarchy::getTopLevelChildren() const
640 {
641     return m_apImpl->getChildren( ObjectHierarchy::getRootNodeOID());
642 }
643 
hasChildren(const tOID & rParent) const644 bool ObjectHierarchy::hasChildren( const tOID& rParent ) const
645 {
646     return m_apImpl->hasChildren( rParent );
647 }
648 
getChildren(const ObjectHierarchy::tOID & rParent) const649 ObjectHierarchy::tChildContainer ObjectHierarchy::getChildren(
650     const ObjectHierarchy::tOID& rParent ) const
651 {
652     if ( rParent.isValid() )
653         return m_apImpl->getChildren( rParent );
654 
655     return ObjectHierarchy::tChildContainer();
656 }
657 
getSiblings(const ObjectHierarchy::tOID & rNode) const658 ObjectHierarchy::tChildContainer ObjectHierarchy::getSiblings(
659     const ObjectHierarchy::tOID& rNode ) const
660 {
661     if ( rNode.isValid() && !isRootNode( rNode ) )
662         return m_apImpl->getSiblings( rNode );
663 
664     return ObjectHierarchy::tChildContainer();
665 }
666 
getParent(const ObjectHierarchy::tOID & rNode) const667 ObjectHierarchy::tOID ObjectHierarchy::getParent(
668     const ObjectHierarchy::tOID& rNode ) const
669 {
670     return m_apImpl->getParent( rNode );
671 }
672 
getIndexInParent(const ObjectHierarchy::tOID & rNode) const673 sal_Int32 ObjectHierarchy::getIndexInParent(
674     const ObjectHierarchy::tOID& rNode ) const
675 {
676     tOID aParentOID( m_apImpl->getParent( rNode ));
677     tChildContainer aChildren( m_apImpl->getChildren( aParentOID ) );
678     tChildContainer::const_iterator aIt( aChildren.begin() );
679     for( sal_Int32 nIndex = 0; aIt != aChildren.end(); ++nIndex, ++aIt )
680     {
681         if ( *aIt == rNode )
682             return nIndex;
683     }
684     return -1;
685 }
686 
687 // ================================================================================
688 
ObjectKeyNavigation(const ObjectHierarchy::tOID & rCurrentOID,const Reference<chart2::XChartDocument> & xChartDocument,ExplicitValueProvider * pExplicitValueProvider)689 ObjectKeyNavigation::ObjectKeyNavigation(
690     const ObjectHierarchy::tOID & rCurrentOID,
691     const Reference< chart2::XChartDocument > & xChartDocument,
692     ExplicitValueProvider * pExplicitValueProvider /* = 0 */ ) :
693         m_aCurrentOID( rCurrentOID ),
694         m_xChartDocument( xChartDocument ),
695         m_pExplicitValueProvider( pExplicitValueProvider ),
696         m_bStepDownInDiagram( true )
697 {
698     if ( !m_aCurrentOID.isValid() )
699     {
700         setCurrentSelection( ObjectHierarchy::getRootNodeOID() );
701     }
702 }
703 
handleKeyEvent(const awt::KeyEvent & rEvent)704 bool ObjectKeyNavigation::handleKeyEvent(
705     const awt::KeyEvent & rEvent )
706 {
707     bool bResult = false;
708 
709     switch( rEvent.KeyCode )
710     {
711         case awt::Key::TAB:
712             if( rEvent.Modifiers & awt::KeyModifier::SHIFT )
713                 bResult = previous();
714             else
715                 bResult = next();
716             break;
717         case awt::Key::HOME:
718             bResult = first();
719             break;
720         case awt::Key::END:
721             bResult = last();
722             break;
723         case awt::Key::F3:
724             if( rEvent.Modifiers & awt::KeyModifier::SHIFT )
725                 bResult = up();
726             else
727                 bResult = down();
728             break;
729         case awt::Key::ESCAPE:
730             setCurrentSelection( ObjectIdentifier() );
731             bResult = true;
732             break;
733         default:
734             bResult = false;
735             break;
736     }
737     return bResult;
738 }
739 
setCurrentSelection(const ObjectHierarchy::tOID & rOID)740 void ObjectKeyNavigation::setCurrentSelection( const ObjectHierarchy::tOID& rOID )
741 {
742     m_aCurrentOID = rOID;
743 }
744 
getCurrentSelection() const745 ObjectHierarchy::tOID ObjectKeyNavigation::getCurrentSelection() const
746 {
747     return m_aCurrentOID;
748 }
749 
first()750 bool ObjectKeyNavigation::first()
751 {
752     ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider, m_bStepDownInDiagram );
753     ObjectHierarchy::tChildContainer aSiblings( aHierarchy.getSiblings( getCurrentSelection() ) );
754     bool bResult = !aSiblings.empty();
755     if( bResult )
756         setCurrentSelection( aSiblings.front());
757     else
758         bResult = veryFirst();
759     return bResult;
760 }
761 
last()762 bool ObjectKeyNavigation::last()
763 {
764     ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider, m_bStepDownInDiagram );
765     ObjectHierarchy::tChildContainer aSiblings( aHierarchy.getSiblings( getCurrentSelection() ) );
766     bool bResult = !aSiblings.empty();
767     if( bResult )
768         setCurrentSelection( aSiblings.back());
769     else
770         bResult = veryLast();
771     return bResult;
772 }
773 
next()774 bool ObjectKeyNavigation::next()
775 {
776     ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider, m_bStepDownInDiagram );
777     ObjectHierarchy::tChildContainer aSiblings( aHierarchy.getSiblings( getCurrentSelection() ) );
778     bool bResult = !aSiblings.empty();
779     if( bResult )
780     {
781         ObjectHierarchy::tChildContainer::const_iterator aIt(
782             ::std::find( aSiblings.begin(), aSiblings.end(), getCurrentSelection()));
783         OSL_ASSERT( aIt != aSiblings.end());
784         if( ++aIt == aSiblings.end())
785             aIt = aSiblings.begin();
786         setCurrentSelection( *aIt );
787     }
788     else
789         bResult = veryFirst();
790 
791     return bResult;
792 }
793 
previous()794 bool ObjectKeyNavigation::previous()
795 {
796     ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider, m_bStepDownInDiagram );
797     ObjectHierarchy::tChildContainer aSiblings( aHierarchy.getSiblings( getCurrentSelection()));
798     bool bResult = !aSiblings.empty();
799     if( bResult )
800     {
801         ObjectHierarchy::tChildContainer::const_iterator aIt(
802             ::std::find( aSiblings.begin(), aSiblings.end(), getCurrentSelection()));
803         OSL_ASSERT( aIt != aSiblings.end());
804         if( aIt == aSiblings.begin())
805             aIt = aSiblings.end();
806         --aIt;
807         setCurrentSelection( *aIt );
808     }
809     else
810         bResult = veryLast();
811     return bResult;
812 }
813 
up()814 bool ObjectKeyNavigation::up()
815 {
816     ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider, m_bStepDownInDiagram );
817     bool bResult = !ObjectHierarchy::isRootNode( getCurrentSelection());
818     if( bResult )
819         setCurrentSelection( aHierarchy.getParent( getCurrentSelection()));
820     return bResult;
821 }
822 
down()823 bool ObjectKeyNavigation::down()
824 {
825     ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider, m_bStepDownInDiagram );
826     bool bResult = aHierarchy.hasChildren( getCurrentSelection());
827     if( bResult )
828     {
829         ObjectHierarchy::tChildContainer aChildren = aHierarchy.getChildren( getCurrentSelection());
830         OSL_ASSERT( !aChildren.empty());
831         setCurrentSelection( aChildren.front());
832     }
833     return bResult;
834 }
835 
veryFirst()836 bool ObjectKeyNavigation::veryFirst()
837 {
838     ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider, m_bStepDownInDiagram );
839     ObjectHierarchy::tChildContainer aChildren( aHierarchy.getTopLevelChildren());
840     bool bResult = !aChildren.empty();
841     if( bResult )
842         setCurrentSelection( aChildren.front());
843     return bResult;
844 }
845 
veryLast()846 bool ObjectKeyNavigation::veryLast()
847 {
848     ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider, m_bStepDownInDiagram );
849     ObjectHierarchy::tChildContainer aChildren( aHierarchy.getTopLevelChildren());
850     bool bResult = !aChildren.empty();
851     if( bResult )
852         setCurrentSelection( aChildren.back());
853     return bResult;
854 }
855 
856 } //  namespace chart
857