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