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 "ChartTypeTemplate.hxx"
21 #include "DataInterpreter.hxx"
22 #include <CommonConverters.hxx>
23 #include <ChartTypeHelper.hxx>
24 
25 #include <AxisHelper.hxx>
26 #include <DiagramHelper.hxx>
27 #include <AxisIndexDefines.hxx>
28 #include <unonames.hxx>
29 
30 #include <com/sun/star/uno/XComponentContext.hpp>
31 #include <com/sun/star/chart2/AxisType.hpp>
32 #include <com/sun/star/chart2/StackingDirection.hpp>
33 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
34 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
35 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
36 #include <tools/diagnose_ex.h>
37 #include <comphelper/property.hxx>
38 #include <comphelper/sequence.hxx>
39 
40 #include <algorithm>
41 
42 using namespace ::com::sun::star;
43 using namespace ::com::sun::star::chart2;
44 
45 using ::com::sun::star::uno::Sequence;
46 using ::com::sun::star::uno::Reference;
47 
48 namespace
49 {
50 
lcl_applyDefaultStyle(const Reference<XDataSeries> & xSeries,sal_Int32 nIndex,const Reference<XDiagram> & xDiagram)51 void lcl_applyDefaultStyle(
52     const Reference< XDataSeries > & xSeries,
53     sal_Int32 nIndex,
54     const Reference< XDiagram > & xDiagram )
55 {
56     // @deprecated: correct default color should be found by view without
57     // setting color as hard attribute
58     if( xSeries.is() && xDiagram.is())
59     {
60         Reference< beans::XPropertySet > xSeriesProp( xSeries, uno::UNO_QUERY );
61         Reference< chart2::XColorScheme > xColorScheme( xDiagram->getDefaultColorScheme());
62         if( xSeriesProp.is() && xColorScheme.is() )
63             xSeriesProp->setPropertyValue(
64                 "Color",
65                 uno::Any( xColorScheme->getColorByIndex( nIndex )));
66     }
67 }
68 
lcl_ensureCorrectLabelPlacement(const Reference<beans::XPropertySet> & xProp,const uno::Sequence<sal_Int32> & rAvailablePlacements)69 void lcl_ensureCorrectLabelPlacement( const Reference< beans::XPropertySet >& xProp, const uno::Sequence < sal_Int32 >& rAvailablePlacements )
70 {
71     sal_Int32 nLabelPlacement=0;
72     if( xProp.is() && (xProp->getPropertyValue( "LabelPlacement" ) >>= nLabelPlacement) )
73     {
74         bool bValid = false;
75         for( sal_Int32 nN = 0; nN < rAvailablePlacements.getLength(); nN++ )
76         {
77             if( rAvailablePlacements[nN] == nLabelPlacement )
78             {
79                 bValid = true;
80                 break;
81             }
82         }
83         if( !bValid )
84         {
85             uno::Any aNewValue;
86             //otherwise use the first supported one
87             if( rAvailablePlacements.hasElements() )
88                 aNewValue <<=rAvailablePlacements[0];
89             xProp->setPropertyValue( "LabelPlacement", aNewValue );
90         }
91     }
92 }
93 
lcl_resetLabelPlacementIfDefault(const Reference<beans::XPropertySet> & xProp,sal_Int32 nDefaultPlacement)94 void lcl_resetLabelPlacementIfDefault( const Reference< beans::XPropertySet >& xProp, sal_Int32 nDefaultPlacement )
95 {
96 
97     sal_Int32 nLabelPlacement=0;
98     if( xProp.is() && (xProp->getPropertyValue( "LabelPlacement" ) >>= nLabelPlacement) )
99     {
100         if( nDefaultPlacement == nLabelPlacement )
101             xProp->setPropertyValue( "LabelPlacement", uno::Any() );
102     }
103 }
104 
lcl_ensureCorrectMissingValueTreatment(const Reference<chart2::XDiagram> & xDiagram,const Reference<XChartType> & xChartType)105 void lcl_ensureCorrectMissingValueTreatment( const Reference< chart2::XDiagram >& xDiagram, const Reference< XChartType >& xChartType )
106 {
107     Reference< beans::XPropertySet > xDiaProp( xDiagram, uno::UNO_QUERY );
108     if( xDiaProp.is() )
109     {
110         uno::Sequence < sal_Int32 > aAvailableMissingValueTreatment(
111             ::chart::ChartTypeHelper::getSupportedMissingValueTreatments( xChartType ) );
112 
113         if( aAvailableMissingValueTreatment.hasElements() )
114             xDiaProp->setPropertyValue( "MissingValueTreatment", uno::Any( aAvailableMissingValueTreatment[0] ) );
115         else
116             xDiaProp->setPropertyValue( "MissingValueTreatment", uno::Any() );
117     }
118 }
119 
120 } // anonymous namespace
121 
122 namespace chart
123 {
124 
ChartTypeTemplate(Reference<uno::XComponentContext> const & xContext,const OUString & rServiceName)125 ChartTypeTemplate::ChartTypeTemplate(
126     Reference< uno::XComponentContext > const & xContext,
127     const OUString & rServiceName ) :
128         m_xContext( xContext ),
129         m_aServiceName( rServiceName )
130 {
131 }
132 
~ChartTypeTemplate()133 ChartTypeTemplate::~ChartTypeTemplate()
134 {}
135 
136 // ____ XChartTypeTemplate ____
createDiagramByDataSource(const uno::Reference<data::XDataSource> & xDataSource,const uno::Sequence<beans::PropertyValue> & aArguments)137 uno::Reference< XDiagram > SAL_CALL ChartTypeTemplate::createDiagramByDataSource(
138     const uno::Reference< data::XDataSource >& xDataSource,
139     const uno::Sequence< beans::PropertyValue >& aArguments )
140 {
141     Reference< XDiagram > xDia;
142 
143     try
144     {
145         // create diagram
146         xDia.set(
147             GetComponentContext()->getServiceManager()->createInstanceWithContext(
148                 "com.sun.star.chart2.Diagram",
149                 GetComponentContext() ),
150             uno::UNO_QUERY_THROW );
151 
152         // modify diagram
153         Reference< chart2::XDataInterpreter > xInterpreter( getDataInterpreter());
154         chart2::InterpretedData aData(
155             xInterpreter->interpretDataSource(
156                 xDataSource, aArguments, Sequence< Reference< XDataSeries > >() ));
157 
158         Sequence< Sequence< Reference< XDataSeries > > > aSeries( aData.Series );
159         sal_Int32 i, j, nCount = 0;
160         for( i=0; i<aSeries.getLength(); ++i )
161         {
162             for( j=0; j<aSeries[i].getLength(); ++j, ++nCount )
163                 lcl_applyDefaultStyle( aSeries[i][j], nCount, xDia );
164         }
165 
166         Sequence< Reference< XChartType > > aOldChartTypesSeq;
167         FillDiagram( xDia, aData.Series, aData.Categories, aOldChartTypesSeq );
168     }
169     catch( const uno::Exception & )
170     {
171         DBG_UNHANDLED_EXCEPTION("chart2");
172     }
173 
174     return xDia;
175 }
176 
supportsCategories()177 sal_Bool SAL_CALL ChartTypeTemplate::supportsCategories()
178 {
179     return true;
180 }
181 
changeDiagram(const uno::Reference<XDiagram> & xDiagram)182 void SAL_CALL ChartTypeTemplate::changeDiagram( const uno::Reference< XDiagram >& xDiagram )
183 {
184     if( ! xDiagram.is())
185         return;
186 
187     try
188     {
189         Sequence< Sequence< Reference< XDataSeries > > > aSeriesSeq(
190             DiagramHelper::getDataSeriesGroups( xDiagram ));
191         Sequence< Reference< XDataSeries > > aFlatSeriesSeq( FlattenSequence( aSeriesSeq ));
192         const sal_Int32 nFormerSeriesCount = aFlatSeriesSeq.getLength();
193 
194         // chart-type specific interpretation of existing data series
195         Reference< chart2::XDataInterpreter > xInterpreter( getDataInterpreter());
196         chart2::InterpretedData aData;
197         aData.Series = aSeriesSeq;
198         aData.Categories = DiagramHelper::getCategoriesFromDiagram( xDiagram );
199 
200         if( xInterpreter->isDataCompatible( aData ) )
201         {
202             aData = xInterpreter->reinterpretDataSeries( aData );
203         }
204         else
205         {
206             Reference< data::XDataSource > xSource( xInterpreter->mergeInterpretedData( aData ));
207             // todo: get a "range-union" from the data provider by calling
208             // OUString aRange = getRangeRepresentationByData( xSource );
209             // xSource.set( getDataByRangeRepresentation( aRange, aParam ));
210             // where aParam == ??
211             Sequence< beans::PropertyValue > aParam;
212             if( aData.Categories.is())
213             {
214                 aParam.realloc( 1 );
215                 aParam[0] = beans::PropertyValue( "HasCategories", -1, uno::Any( true ),
216                                                   beans::PropertyState_DIRECT_VALUE );
217             }
218             aData = xInterpreter->interpretDataSource( xSource, aParam, aFlatSeriesSeq );
219         }
220         aSeriesSeq = aData.Series;
221 
222         sal_Int32 i, j, nIndex = 0;
223         for( i=0; i<aSeriesSeq.getLength(); ++i )
224             for( j=0; j<aSeriesSeq[i].getLength(); ++j, ++nIndex )
225             {
226                 if( nIndex >= nFormerSeriesCount )
227                     lcl_applyDefaultStyle( aSeriesSeq[i][j], nIndex, xDiagram );
228             }
229 
230         // remove charttype groups from all coordinate systems
231         Sequence< Reference< XChartType > > aOldChartTypesSeq(
232             DiagramHelper::getChartTypesFromDiagram(xDiagram) );
233 
234         Reference< XCoordinateSystemContainer > xCoordSysCnt( xDiagram, uno::UNO_QUERY );
235         OSL_ASSERT( xCoordSysCnt.is());
236         if( xCoordSysCnt.is())
237         {
238             Sequence< Reference< XCoordinateSystem > > aCooSysSeq(
239                 xCoordSysCnt->getCoordinateSystems());
240             for( sal_Int32 nCooSysIdx = 0; nCooSysIdx < aCooSysSeq.getLength(); ++nCooSysIdx )
241             {
242                 Reference< XChartTypeContainer > xContainer( aCooSysSeq[nCooSysIdx], uno::UNO_QUERY );
243                 if( xContainer.is() )
244                     xContainer->setChartTypes( Sequence< Reference< XChartType > >() );
245             }
246         }
247 
248         FillDiagram( xDiagram, aSeriesSeq, aData.Categories, aOldChartTypesSeq );
249     }
250     catch( const uno::Exception & )
251     {
252         DBG_UNHANDLED_EXCEPTION("chart2");
253     }
254 }
255 
changeDiagramData(const Reference<chart2::XDiagram> & xDiagram,const Reference<chart2::data::XDataSource> & xDataSource,const Sequence<beans::PropertyValue> & aArguments)256 void SAL_CALL ChartTypeTemplate::changeDiagramData(
257     const Reference< chart2::XDiagram >& xDiagram,
258     const Reference< chart2::data::XDataSource >& xDataSource,
259     const Sequence< beans::PropertyValue >& aArguments )
260 {
261     if( ! (xDiagram.is() &&
262            xDataSource.is()) )
263         return;
264 
265     try
266     {
267         // interpret new data and re-use existing series
268         Sequence< Reference< XDataSeries > > aFlatSeriesSeq(
269             comphelper::containerToSequence( DiagramHelper::getDataSeriesFromDiagram( xDiagram )));
270         const sal_Int32 nFormerSeriesCount = aFlatSeriesSeq.getLength();
271         Reference< chart2::XDataInterpreter > xInterpreter( getDataInterpreter());
272         chart2::InterpretedData aData =
273             xInterpreter->interpretDataSource( xDataSource, aArguments, aFlatSeriesSeq );
274 
275         // data series
276         Sequence< Sequence< Reference< XDataSeries > > > aSeriesSeq( aData.Series );
277 
278         sal_Int32 i, j, nIndex = 0;
279         for( i=0; i<aSeriesSeq.getLength(); ++i )
280             for( j=0; j<aSeriesSeq[i].getLength(); ++j, ++nIndex )
281             {
282                 if( nIndex >= nFormerSeriesCount )
283                 {
284                     lcl_applyDefaultStyle( aSeriesSeq[i][j], nIndex, xDiagram );
285                     applyStyle( aSeriesSeq[i][j], i, j, aSeriesSeq[i].getLength() );
286                 }
287             }
288 
289         // categories
290         DiagramHelper::setCategoriesToDiagram( aData.Categories, xDiagram, true, supportsCategories() );
291 
292         Sequence< Reference< XChartType > > aChartTypes(
293             DiagramHelper::getChartTypesFromDiagram( xDiagram ));
294         sal_Int32 nMax = std::min( aChartTypes.getLength(), aSeriesSeq.getLength());
295         for( i=0; i<nMax; ++i )
296         {
297             Reference< XDataSeriesContainer > xDSCnt( aChartTypes[i], uno::UNO_QUERY_THROW );
298             xDSCnt->setDataSeries( aSeriesSeq[i] );
299         }
300     }
301     catch( const uno::Exception & )
302     {
303         DBG_UNHANDLED_EXCEPTION("chart2");
304     }
305 }
306 
matchesTemplate(const Reference<chart2::XDiagram> & xDiagram,sal_Bool)307 sal_Bool SAL_CALL ChartTypeTemplate::matchesTemplate(
308     const Reference< chart2::XDiagram >& xDiagram,
309     sal_Bool /* bAdaptProperties */ )
310 {
311     bool bResult = false;
312 
313     if( ! xDiagram.is())
314         return bResult;
315 
316     try
317     {
318         Reference< XCoordinateSystemContainer > xCooSysCnt(
319             xDiagram, uno::UNO_QUERY_THROW );
320         Sequence< Reference< XCoordinateSystem > > aCooSysSeq(
321             xCooSysCnt->getCoordinateSystems());
322 
323         // need to have at least one coordinate system
324         bResult = aCooSysSeq.hasElements();
325         if( bResult )
326         {
327             Sequence< Reference< XChartType > > aFormerlyUsedChartTypes;
328             Reference<XChartType> xOldCT = getChartTypeForNewSeries(aFormerlyUsedChartTypes);
329             if (!xOldCT.is())
330                 return false;
331 
332             const OUString aChartTypeToMatch = xOldCT->getChartType();
333             const sal_Int32 nDimensionToMatch = getDimension();
334             for( sal_Int32 nCooSysIdx=0; bResult && (nCooSysIdx < aCooSysSeq.getLength()); ++nCooSysIdx )
335             {
336                 // match dimension
337                 bResult = bResult && (aCooSysSeq[nCooSysIdx]->getDimension() == nDimensionToMatch);
338 
339                 Reference< XChartTypeContainer > xCTCnt( aCooSysSeq[nCooSysIdx], uno::UNO_QUERY_THROW );
340                 Sequence< Reference< XChartType > > aChartTypeSeq( xCTCnt->getChartTypes());
341                 for( sal_Int32 nCTIdx=0; bResult && (nCTIdx < aChartTypeSeq.getLength()); ++nCTIdx )
342                 {
343                     if (!aChartTypeSeq[nCTIdx].is())
344                         return false;
345 
346                     // match chart type
347                     bResult = bResult && aChartTypeSeq[nCTIdx]->getChartType() == aChartTypeToMatch;
348                     bool bFound=false;
349                     bool bAmbiguous=false;
350                     // match stacking mode
351                     bResult = bResult &&
352                         ( DiagramHelper::getStackModeFromChartType(
353                             aChartTypeSeq[nCTIdx], bFound, bAmbiguous,
354                             aCooSysSeq[nCooSysIdx] )
355                           == getStackMode( nCTIdx ) );
356                 }
357             }
358         }
359     }
360     catch( const uno::Exception & )
361     {
362         DBG_UNHANDLED_EXCEPTION("chart2");
363     }
364 
365     return bResult;
366 }
367 
getDataInterpreter()368 Reference< chart2::XDataInterpreter > SAL_CALL ChartTypeTemplate::getDataInterpreter()
369 {
370     if( ! m_xDataInterpreter.is())
371         m_xDataInterpreter.set( new DataInterpreter );
372 
373     return m_xDataInterpreter;
374 }
375 
applyStyle(const Reference<chart2::XDataSeries> & xSeries,::sal_Int32 nChartTypeIndex,::sal_Int32,::sal_Int32)376 void SAL_CALL ChartTypeTemplate::applyStyle(
377     const Reference< chart2::XDataSeries >& xSeries,
378     ::sal_Int32 nChartTypeIndex,
379     ::sal_Int32 /* nSeriesIndex */,
380     ::sal_Int32 /* nSeriesCount */ )
381 {
382     // sset stacking mode
383     Reference< beans::XPropertySet > xSeriesProp( xSeries, uno::UNO_QUERY );
384     if( xSeriesProp.is())
385     {
386         try
387         {
388             StackMode eStackMode = getStackMode( nChartTypeIndex );
389             const uno::Any aPropValue(
390                 ( (eStackMode == StackMode::YStacked) ||
391                   (eStackMode == StackMode::YStackedPercent) )
392                 ? chart2::StackingDirection_Y_STACKING
393                 : (eStackMode == StackMode::ZStacked )
394                 ? chart2::StackingDirection_Z_STACKING
395                 : chart2::StackingDirection_NO_STACKING );
396             xSeriesProp->setPropertyValue( "StackingDirection", aPropValue );
397 
398             //ensure valid label placement
399             {
400                 uno::Sequence < sal_Int32 > aAvailablePlacements( ChartTypeHelper::getSupportedLabelPlacements(
401                             getChartTypeForIndex( nChartTypeIndex ), isSwapXAndY(), xSeries ) );
402                 lcl_ensureCorrectLabelPlacement( xSeriesProp, aAvailablePlacements );
403 
404                 uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
405                 if( xSeriesProp->getPropertyValue( "AttributedDataPoints" ) >>= aAttributedDataPointIndexList )
406                     for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
407                         lcl_ensureCorrectLabelPlacement( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]), aAvailablePlacements );
408             }
409         }
410         catch( const uno::Exception & )
411         {
412             DBG_UNHANDLED_EXCEPTION("chart2");
413         }
414     }
415 }
416 
applyStyles(const Reference<chart2::XDiagram> & xDiagram)417 void ChartTypeTemplate::applyStyles( const Reference< chart2::XDiagram >& xDiagram )
418 {
419     // apply chart-type specific styles, like "symbols on" for example
420     Sequence< Sequence< Reference< XDataSeries > > > aNewSeriesSeq(
421         DiagramHelper::getDataSeriesGroups( xDiagram ));
422     for( sal_Int32 i=0; i<aNewSeriesSeq.getLength(); ++i )
423     {
424         const sal_Int32 nNumSeries = aNewSeriesSeq[i].getLength();
425         for( sal_Int32 j=0; j<nNumSeries; ++j )
426             applyStyle( aNewSeriesSeq[i][j], i, j, nNumSeries );
427     }
428 
429     //ensure valid empty cell handling (for first chart type...)
430     lcl_ensureCorrectMissingValueTreatment( xDiagram, getChartTypeForIndex( 0 ) );
431 }
432 
resetStyles(const Reference<chart2::XDiagram> & xDiagram)433 void SAL_CALL ChartTypeTemplate::resetStyles( const Reference< chart2::XDiagram >& xDiagram )
434 {
435     // reset number format if we had percent stacking on
436     bool bPercent = (getStackMode(0) == StackMode::YStackedPercent);
437     if( bPercent )
438     {
439         Sequence< Reference< chart2::XAxis > > aAxisSeq( AxisHelper::getAllAxesOfDiagram( xDiagram ) );
440         for( sal_Int32 i=0; i<aAxisSeq.getLength(); ++i )
441         {
442             if( AxisHelper::getDimensionIndexOfAxis( aAxisSeq[i], xDiagram )== 1 )
443             {
444                 Reference< beans::XPropertySet > xAxisProp( aAxisSeq[i], uno::UNO_QUERY );
445                 if( xAxisProp.is())
446                 {
447                     // set number format to source format
448                     xAxisProp->setPropertyValue(CHART_UNONAME_LINK_TO_SRC_NUMFMT, uno::Any(true));
449                     xAxisProp->setPropertyValue(CHART_UNONAME_NUMFMT, uno::Any());
450                 }
451             }
452         }
453     }
454 
455     //reset label placement if default
456     {
457         uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );
458         if( xCooSysContainer.is() )
459         {
460             uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
461             for( sal_Int32 nCS = 0; nCS < aCooSysList.getLength(); ++nCS )
462             {
463                 uno::Reference< XCoordinateSystem > xCooSys( aCooSysList[nCS] );
464 
465                 //iterate through all chart types in the current coordinate system
466                 uno::Reference< XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY );
467                 OSL_ASSERT( xChartTypeContainer.is());
468                 if( !xChartTypeContainer.is() )
469                     continue;
470                 uno::Sequence< uno::Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
471                 for( sal_Int32 nT = 0; nT < aChartTypeList.getLength(); ++nT )
472                 {
473                     uno::Reference< XChartType > xChartType( aChartTypeList[nT] );
474 
475                     //iterate through all series in this chart type
476                     uno::Reference< XDataSeriesContainer > xDataSeriesContainer( xChartType, uno::UNO_QUERY );
477                     OSL_ASSERT( xDataSeriesContainer.is());
478                     if( !xDataSeriesContainer.is() )
479                         continue;
480 
481                     uno::Sequence< uno::Reference< XDataSeries > > aSeriesList( xDataSeriesContainer->getDataSeries() );
482                     for( sal_Int32 nS = 0; nS < aSeriesList.getLength(); ++nS )
483                     {
484                         Reference< XDataSeries > xSeries(aSeriesList[nS]);
485                         Reference< beans::XPropertySet > xSeriesProp( xSeries, uno::UNO_QUERY );
486                         if(!xSeries.is() || !xSeriesProp.is() )
487                             continue;
488 
489                         uno::Sequence < sal_Int32 > aAvailablePlacements( ChartTypeHelper::getSupportedLabelPlacements(
490                             xChartType, isSwapXAndY(), xSeries ) );
491                         if(!aAvailablePlacements.hasElements())
492                             continue;
493 
494                         sal_Int32 nDefaultPlacement = aAvailablePlacements[0];
495 
496                         lcl_resetLabelPlacementIfDefault( xSeriesProp, nDefaultPlacement );
497 
498                         uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
499                         if( xSeriesProp->getPropertyValue( "AttributedDataPoints" ) >>= aAttributedDataPointIndexList )
500                             for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
501                                 lcl_resetLabelPlacementIfDefault( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]), nDefaultPlacement );
502                     }
503                 }
504             }
505         }
506     }
507 
508 }
509 
510 // ____ XServiceName ____
getServiceName()511     OUString SAL_CALL ChartTypeTemplate::getServiceName()
512 {
513     return m_aServiceName;
514 }
515 
getDimension() const516 sal_Int32 ChartTypeTemplate::getDimension() const
517 {
518     return 2;
519 }
520 
getStackMode(sal_Int32) const521 StackMode ChartTypeTemplate::getStackMode( sal_Int32 /* nChartTypeIndex */ ) const
522 {
523     return StackMode::NONE;
524 }
525 
isSwapXAndY() const526 bool ChartTypeTemplate::isSwapXAndY() const
527 {
528     return false;
529 }
530 
createCoordinateSystems(const Reference<chart2::XCoordinateSystemContainer> & xOutCooSysCnt)531 void ChartTypeTemplate::createCoordinateSystems(
532     const Reference< chart2::XCoordinateSystemContainer > & xOutCooSysCnt )
533 {
534     if( ! xOutCooSysCnt.is())
535         return;
536     Sequence< Reference< XChartType > > aFormerlyUsedChartTypes;
537     Reference< XChartType > xChartType( getChartTypeForNewSeries(aFormerlyUsedChartTypes));
538     if( ! xChartType.is())
539         return;
540     Reference< XCoordinateSystem > xCooSys( xChartType->createCoordinateSystem( getDimension()));
541     if( ! xCooSys.is())
542     {
543         // chart type wants no coordinate systems
544         xOutCooSysCnt->setCoordinateSystems( Sequence< Reference< XCoordinateSystem > >());
545         return;
546     }
547     // #i69680# make grid of first y-axis visible (was in the CooSys CTOR before)
548     if( xCooSys->getDimension() >= 2 )
549     {
550         Reference< chart2::XAxis > xAxis( xCooSys->getAxisByDimension( 1, 0 ));
551         if( xAxis.is())
552             AxisHelper::makeGridVisible( xAxis->getGridProperties() );
553     }
554 
555     Sequence< Reference< XCoordinateSystem > > aCoordinateSystems(
556         xOutCooSysCnt->getCoordinateSystems());
557 
558     if( aCoordinateSystems.hasElements())
559     {
560         bool bOk = true;
561         for( sal_Int32 i=0; bOk && i<aCoordinateSystems.getLength(); ++i )
562             bOk = bOk && ( xCooSys->getCoordinateSystemType() == aCoordinateSystems[i]->getCoordinateSystemType() &&
563                            (xCooSys->getDimension() == aCoordinateSystems[i]->getDimension()) );
564         // coordinate systems are ok
565         if( bOk )
566             return;
567         // there are coordinate systems but they do not fit.  So overwrite them.
568     }
569 
570     //copy as much info from former coordinate system as possible:
571     if( aCoordinateSystems.hasElements() )
572     {
573         Reference< XCoordinateSystem > xOldCooSys( aCoordinateSystems[0] );
574         sal_Int32 nMaxDimensionCount = std::min( xCooSys->getDimension(), xOldCooSys->getDimension() );
575 
576         for(sal_Int32 nDimensionIndex=0; nDimensionIndex<nMaxDimensionCount; nDimensionIndex++)
577         {
578             const sal_Int32 nMaximumAxisIndex = xOldCooSys->getMaximumAxisIndexByDimension(nDimensionIndex);
579             for(sal_Int32 nAxisIndex=0; nAxisIndex<=nMaximumAxisIndex; ++nAxisIndex)
580             {
581                 uno::Reference< XAxis > xAxis( xOldCooSys->getAxisByDimension( nDimensionIndex, nAxisIndex ) );
582                 if( xAxis.is())
583                 {
584                     xCooSys->setAxisByDimension( nDimensionIndex, xAxis, nAxisIndex );
585                 }
586             }
587         }
588     }
589 
590     // set new coordinate systems
591     aCoordinateSystems.realloc( 1 );
592     aCoordinateSystems[0] = xCooSys;
593 
594     xOutCooSysCnt->setCoordinateSystems( aCoordinateSystems );
595 }
596 
adaptScales(const Sequence<Reference<chart2::XCoordinateSystem>> & aCooSysSeq,const Reference<data::XLabeledDataSequence> & xCategories)597 void ChartTypeTemplate::adaptScales(
598     const Sequence< Reference< chart2::XCoordinateSystem > > & aCooSysSeq,
599     const Reference< data::XLabeledDataSequence > & xCategories //@todo: in future there may be more than one sequence of categories (e.g. charttype with categories at x and y axis )
600     )
601 {
602     bool bSupportsCategories( supportsCategories() );
603     for( sal_Int32 nCooSysIdx=0; nCooSysIdx<aCooSysSeq.getLength(); ++nCooSysIdx )
604     {
605         try
606         {
607             Reference< XCoordinateSystem > xCooSys( aCooSysSeq[nCooSysIdx] );
608             if( !xCooSys.is() )
609                 continue;
610 
611             // attach categories to first axis
612             sal_Int32 nDim( xCooSys->getDimension());
613             if( nDim > 0 )
614             {
615                 const sal_Int32 nDimensionX = 0;
616                 const sal_Int32 nMaxIndex = xCooSys->getMaximumAxisIndexByDimension(nDimensionX);
617                 for(sal_Int32 nI=0; nI<=nMaxIndex; ++nI)
618                 {
619                     Reference< XAxis > xAxis( xCooSys->getAxisByDimension(nDimensionX,nI) );
620                     if( xAxis.is())
621                     {
622                         ScaleData aData( xAxis->getScaleData() );
623                         aData.Categories = xCategories;
624                         if(bSupportsCategories)
625                         {
626                             Reference< XChartType > xChartType(getChartTypeForNewSeries(Sequence< Reference< XChartType > >()));
627                             if( aData.AxisType == AxisType::CATEGORY )
628                             {
629                                 aData.ShiftedCategoryPosition = m_aServiceName.indexOf("Column") != -1 || m_aServiceName.indexOf("Bar") != -1 || m_aServiceName.endsWith("Close");
630                             }
631                             bool bSupportsDates = ::chart::ChartTypeHelper::isSupportingDateAxis( xChartType, nDimensionX );
632                             if( aData.AxisType != AxisType::CATEGORY && ( aData.AxisType != AxisType::DATE || !bSupportsDates) )
633                             {
634                                 aData.AxisType = AxisType::CATEGORY;
635                                 aData.AutoDateAxis = true;
636                                 AxisHelper::removeExplicitScaling( aData );
637                             }
638                         }
639                         else
640                             aData.AxisType = AxisType::REALNUMBER;
641 
642                         xAxis->setScaleData( aData );
643                     }
644                 }
645             }
646             // set percent stacking mode at second axis
647             if( nDim > 1 )
648             {
649                 const sal_Int32 nMaxIndex = xCooSys->getMaximumAxisIndexByDimension(1);
650                 for(sal_Int32 nI=0; nI<=nMaxIndex; ++nI)
651                 {
652                     Reference< chart2::XAxis > xAxis( xCooSys->getAxisByDimension( 1,nI ));
653                     if( xAxis.is())
654                     {
655                         bool bPercent = (getStackMode(0) == StackMode::YStackedPercent);
656                         chart2::ScaleData aScaleData = xAxis->getScaleData();
657 
658                         if( bPercent != (aScaleData.AxisType==AxisType::PERCENT) )
659                         {
660                             if( bPercent )
661                                 aScaleData.AxisType = AxisType::PERCENT;
662                             else
663                                 aScaleData.AxisType = AxisType::REALNUMBER;
664                             xAxis->setScaleData( aScaleData );
665                         }
666                     }
667                 }
668             }
669         }
670         catch( const uno::Exception & )
671         {
672             DBG_UNHANDLED_EXCEPTION("chart2");
673         }
674     }
675 }
676 
adaptDiagram(const Reference<XDiagram> &)677 void ChartTypeTemplate::adaptDiagram( const Reference< XDiagram > & /* xDiagram */ )
678 {
679     }
680 
createAxes(const Sequence<Reference<XCoordinateSystem>> & rCoordSys)681 void ChartTypeTemplate::createAxes(
682     const Sequence< Reference< XCoordinateSystem > > & rCoordSys )
683 {
684     //create missing axes
685     if( rCoordSys.hasElements() )
686     {
687         Reference< XCoordinateSystem > xCooSys( rCoordSys[0] );
688         if(!xCooSys.is())
689             return;
690 
691         //create main axis in first coordinate system
692         sal_Int32 nDimCount = xCooSys->getDimension();
693         sal_Int32 nDim=0;
694         for( nDim=0; nDim<nDimCount; ++nDim )
695         {
696             sal_Int32 nAxisCount = getAxisCountByDimension( nDim );
697             if( nDim == 1 &&
698                 nAxisCount < 2 && AxisHelper::isSecondaryYAxisNeeded( xCooSys ))
699                 nAxisCount = 2;
700             for( sal_Int32 nAxisIndex = 0; nAxisIndex < nAxisCount; ++nAxisIndex )
701             {
702                 Reference< XAxis > xAxis = AxisHelper::getAxis( nDim, nAxisIndex, xCooSys );
703                 if( !xAxis.is())
704                 {
705                     // create and add axis
706                     xAxis.set( AxisHelper::createAxis(
707                                    nDim, nAxisIndex, xCooSys, GetComponentContext() ));
708                 }
709             }
710         }
711     }
712 }
713 
adaptAxes(const Sequence<Reference<XCoordinateSystem>> & rCoordSys)714 void ChartTypeTemplate::adaptAxes(
715     const Sequence< Reference< XCoordinateSystem > > & rCoordSys )
716 {
717     //adapt properties of existing axes and remove superfluous axes
718 
719     if( rCoordSys.hasElements() )
720     {
721         for( sal_Int32 nCooSysIdx=0; nCooSysIdx < rCoordSys.getLength(); ++nCooSysIdx )
722         {
723             Reference< XCoordinateSystem > xCooSys( rCoordSys[nCooSysIdx] );
724             if( !xCooSys.is() )
725                 continue;
726             sal_Int32 nDimCount = xCooSys->getDimension();
727             for( sal_Int32 nDim=0; nDim<nDimCount; ++nDim )
728             {
729                 sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension( nDim );
730                 for( sal_Int32 nAxisIndex=0; nAxisIndex<=nMaxAxisIndex; nAxisIndex++ )
731                 {
732                     Reference< XAxis > xAxis( AxisHelper::getAxis( nDim, nAxisIndex, xCooSys ) );
733                     if( !xAxis.is() )
734                         continue;
735 
736                     if( nAxisIndex == MAIN_AXIS_INDEX || nAxisIndex == SECONDARY_AXIS_INDEX )
737                     {
738                         // adapt scales
739                         bool bPercent = (getStackMode(0) == StackMode::YStackedPercent);
740                         if( bPercent && nDim == 1 )
741                         {
742                             Reference< beans::XPropertySet > xAxisProp( xAxis, uno::UNO_QUERY );
743                             if( xAxisProp.is())
744                             {
745                                 // set number format to source format
746                                 xAxisProp->setPropertyValue(CHART_UNONAME_LINK_TO_SRC_NUMFMT, uno::Any(true));
747                                 xAxisProp->setPropertyValue(CHART_UNONAME_NUMFMT, uno::Any());
748                             }
749                         }
750                     }
751                 }
752             }
753         }
754     }
755 }
756 
getAxisCountByDimension(sal_Int32 nDimension)757 sal_Int32 ChartTypeTemplate::getAxisCountByDimension( sal_Int32 nDimension )
758 {
759     return (nDimension < getDimension()) ? 1 : 0;
760 }
761 
FillDiagram(const Reference<XDiagram> & xDiagram,const Sequence<Sequence<Reference<XDataSeries>>> & aSeriesSeq,const Reference<data::XLabeledDataSequence> & xCategories,const Sequence<Reference<XChartType>> & aOldChartTypesSeq)762 void ChartTypeTemplate::FillDiagram(
763     const Reference< XDiagram >& xDiagram,
764     const Sequence< Sequence< Reference< XDataSeries > > >& aSeriesSeq,
765     const Reference< data::XLabeledDataSequence >& xCategories,
766     const Sequence< Reference< XChartType > >& aOldChartTypesSeq )
767 {
768     adaptDiagram( xDiagram );
769 
770     try
771     {
772         // create coordinate systems and scales
773         Reference< XCoordinateSystemContainer > xCoordSysCnt( xDiagram, uno::UNO_QUERY_THROW );
774         createCoordinateSystems( xCoordSysCnt );
775         Sequence< Reference< XCoordinateSystem > > aCoordinateSystems( xCoordSysCnt->getCoordinateSystems());
776         createAxes( aCoordinateSystems );
777         adaptAxes( aCoordinateSystems );
778         adaptScales( aCoordinateSystems, xCategories );
779 
780         // chart types
781         createChartTypes( aSeriesSeq, aCoordinateSystems, aOldChartTypesSeq );
782         applyStyles( xDiagram );
783     }
784     catch( const uno::Exception & )
785     {
786         DBG_UNHANDLED_EXCEPTION("chart2");
787     }
788 }
789 
createChartTypes(const Sequence<Sequence<Reference<XDataSeries>>> & aSeriesSeq,const Sequence<Reference<XCoordinateSystem>> & rCoordSys,const Sequence<Reference<XChartType>> & aOldChartTypesSeq)790 void ChartTypeTemplate::createChartTypes(
791     const Sequence< Sequence< Reference< XDataSeries > > > & aSeriesSeq,
792     const Sequence< Reference< XCoordinateSystem > > & rCoordSys,
793     const Sequence< Reference< XChartType > >& aOldChartTypesSeq )
794 {
795     if( ! rCoordSys.hasElements() ||
796         ! rCoordSys[0].is() )
797         return;
798 
799     try
800     {
801         sal_Int32 nCooSysIdx=0;
802         Reference< XChartType > xCT;
803         if( !aSeriesSeq.hasElements() )
804         {
805             // we need a new chart type
806             xCT.set( getChartTypeForNewSeries( aOldChartTypesSeq ));
807             Reference< XChartTypeContainer > xCTCnt( rCoordSys[nCooSysIdx], uno::UNO_QUERY_THROW );
808             Sequence< Reference< XChartType > > aCTSeq( xCTCnt->getChartTypes());
809             aCTSeq.realloc( 1 );
810             aCTSeq[0] = xCT;
811             xCTCnt->setChartTypes( aCTSeq );
812         }
813         else
814         {
815             for( sal_Int32 nSeriesIdx=0; nSeriesIdx<aSeriesSeq.getLength(); ++nSeriesIdx )
816             {
817                 if( nSeriesIdx == nCooSysIdx )
818                 {
819                     // we need a new chart type
820                     xCT.set( getChartTypeForNewSeries( aOldChartTypesSeq ));
821                     Reference< XChartTypeContainer > xCTCnt( rCoordSys[nCooSysIdx], uno::UNO_QUERY_THROW );
822                     Sequence< Reference< XChartType > > aCTSeq( xCTCnt->getChartTypes());
823                     if( aCTSeq.hasElements())
824                     {
825                         aCTSeq[0] = xCT;
826                         xCTCnt->setChartTypes( aCTSeq );
827                     }
828                     else
829                         xCTCnt->addChartType( xCT );
830 
831                     Reference< chart2::XDataSeriesContainer > xDSCnt( xCT, uno::UNO_QUERY_THROW );
832                     xDSCnt->setDataSeries( aSeriesSeq[nSeriesIdx] );
833                 }
834                 else
835                 {
836                     // reuse existing chart type
837                     OSL_ASSERT( xCT.is());
838                     Reference< chart2::XDataSeriesContainer > xDSCnt( xCT, uno::UNO_QUERY_THROW );
839                     Sequence< Reference< XDataSeries > > aNewSeriesSeq( xDSCnt->getDataSeries());
840                     sal_Int32 nNewStartIndex = aNewSeriesSeq.getLength();
841                     aNewSeriesSeq.realloc( nNewStartIndex + aSeriesSeq[nSeriesIdx].getLength() );
842                     std::copy( aSeriesSeq[nSeriesIdx].begin(),
843                                  aSeriesSeq[nSeriesIdx].end(),
844                                  aNewSeriesSeq.getArray() + nNewStartIndex );
845                     xDSCnt->setDataSeries( aNewSeriesSeq );
846                 }
847 
848                 // spread the series over the available coordinate systems
849                 if( rCoordSys.getLength() > (nCooSysIdx + 1) )
850                     ++nCooSysIdx;
851             }
852         }
853     }
854     catch( const uno::Exception & )
855     {
856         DBG_UNHANDLED_EXCEPTION("chart2");
857     }
858 }
859 
copyPropertiesFromOldToNewCoordinateSystem(const Sequence<Reference<XChartType>> & rOldChartTypesSeq,const Reference<XChartType> & xNewChartType)860 void ChartTypeTemplate::copyPropertiesFromOldToNewCoordinateSystem(
861                     const Sequence< Reference< XChartType > > & rOldChartTypesSeq,
862                     const Reference< XChartType > & xNewChartType )
863 {
864     Reference< beans::XPropertySet > xDestination( xNewChartType, uno::UNO_QUERY );
865     if( !xDestination.is() )
866         return;
867 
868     OUString aNewChartType( xNewChartType->getChartType() );
869 
870     Reference< beans::XPropertySet > xSource;
871     sal_Int32 nN=0;
872     for( nN=0; nN<rOldChartTypesSeq.getLength();++nN)
873     {
874         Reference< XChartType > xOldType( rOldChartTypesSeq[nN] );
875         if( xOldType.is() && xOldType->getChartType() == aNewChartType )
876         {
877             xSource.set( Reference< beans::XPropertySet >(xOldType, uno::UNO_QUERY ) );
878             if( xSource.is() )
879                 break;
880         }
881     }
882     if( xSource.is() )
883         comphelper::copyProperties( xSource, xDestination );
884 }
885 
886 } //  namespace chart
887 
888 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
889