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 <sax/tools/converter.hxx>
21
22 #include "SchXMLAxisContext.hxx"
23 #include "SchXMLChartContext.hxx"
24 #include "SchXMLTools.hxx"
25 #include <xmloff/xmlimp.hxx>
26 #include <xmloff/xmlnamespace.hxx>
27 #include <xmloff/xmlement.hxx>
28 #include <xmloff/xmlstyle.hxx>
29 #include <xmloff/prstylei.hxx>
30 #include <xmloff/namespacemap.hxx>
31 #include <xmloff/xmluconv.hxx>
32
33 #include <rtl/math.hxx>
34 #include <tools/color.hxx>
35 #include <sal/log.hxx>
36
37 #include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
38 #include <com/sun/star/chart/ChartAxisMarkPosition.hpp>
39 #include <com/sun/star/chart/ChartAxisPosition.hpp>
40 #include <com/sun/star/chart/ChartAxisType.hpp>
41 #include <com/sun/star/chart/TimeIncrement.hpp>
42 #include <com/sun/star/chart/TimeInterval.hpp>
43 #include <com/sun/star/chart/TimeUnit.hpp>
44 #include <com/sun/star/chart/XAxis.hpp>
45 #include <com/sun/star/chart/XAxisSupplier.hpp>
46 #include <com/sun/star/chart/XChartDocument.hpp>
47 #include <com/sun/star/chart2/AxisType.hpp>
48 #include <com/sun/star/chart2/XChartDocument.hpp>
49 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
50
51 #include <com/sun/star/drawing/LineStyle.hpp>
52
53 using namespace ::xmloff::token;
54 using namespace com::sun::star;
55
56 using com::sun::star::uno::Reference;
57
58 const SvXMLEnumMapEntry<SchXMLAxisDimension> aXMLAxisDimensionMap[] =
59 {
60 { XML_X, SCH_XML_AXIS_X },
61 { XML_Y, SCH_XML_AXIS_Y },
62 { XML_Z, SCH_XML_AXIS_Z },
63 { XML_TOKEN_INVALID, SchXMLAxisDimension(0) }
64 };
65
66 const SvXMLEnumMapEntry<sal_uInt16> aXMLAxisTypeMap[] =
67 {
68 { XML_AUTO, css::chart::ChartAxisType::AUTOMATIC },
69 { XML_TEXT, css::chart::ChartAxisType::CATEGORY },
70 { XML_DATE, css::chart::ChartAxisType::DATE },
71 { XML_TOKEN_INVALID, 0 }
72 };
73
74 namespace {
75
76 class SchXMLCategoriesContext : public SvXMLImportContext
77 {
78 private:
79 OUString& mrAddress;
80
81 public:
82 SchXMLCategoriesContext( SvXMLImport& rImport,
83 OUString& rAddress );
84 virtual void SAL_CALL startFastElement( sal_Int32 nElement,
85 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
86 };
87
88 class DateScaleContext : public SvXMLImportContext
89 {
90 public:
91 DateScaleContext( SvXMLImport& rImport,
92 const Reference< beans::XPropertySet >& rAxisProps );
93
94 virtual void SAL_CALL startFastElement( sal_Int32 nElement,
95 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
96
97 private:
98 Reference< beans::XPropertySet > m_xAxisProps;
99 };
100
101 }
102
SchXMLAxisContext(SchXMLImportHelper & rImpHelper,SvXMLImport & rImport,Reference<chart::XDiagram> const & xDiagram,std::vector<SchXMLAxis> & rAxes,OUString & rCategoriesAddress,bool bAddMissingXAxisForNetCharts,bool bAdaptWrongPercentScaleValues,bool bAdaptXAxisOrientationForOld2DBarCharts,bool & rbAxisPositionAttributeImported)103 SchXMLAxisContext::SchXMLAxisContext( SchXMLImportHelper& rImpHelper,
104 SvXMLImport& rImport,
105 Reference< chart::XDiagram > const & xDiagram,
106 std::vector< SchXMLAxis >& rAxes,
107 OUString & rCategoriesAddress,
108 bool bAddMissingXAxisForNetCharts,
109 bool bAdaptWrongPercentScaleValues,
110 bool bAdaptXAxisOrientationForOld2DBarCharts,
111 bool& rbAxisPositionAttributeImported ) :
112 SvXMLImportContext( rImport ),
113 m_rImportHelper( rImpHelper ),
114 m_xDiagram( xDiagram ),
115 m_rAxes( rAxes ),
116 m_rCategoriesAddress( rCategoriesAddress ),
117 m_nAxisType(chart::ChartAxisType::AUTOMATIC),
118 m_bAxisTypeImported(false),
119 m_bDateScaleImported(false),
120 m_bAddMissingXAxisForNetCharts( bAddMissingXAxisForNetCharts ),
121 m_bAdaptWrongPercentScaleValues( bAdaptWrongPercentScaleValues ),
122 m_bAdaptXAxisOrientationForOld2DBarCharts( bAdaptXAxisOrientationForOld2DBarCharts ),
123 m_rbAxisPositionAttributeImported( rbAxisPositionAttributeImported )
124 {
125 }
126
~SchXMLAxisContext()127 SchXMLAxisContext::~SchXMLAxisContext()
128 {}
129
lcl_getChartAxis(const SchXMLAxis & rCurrentAxis,const Reference<chart::XDiagram> & rDiagram)130 static Reference< chart::XAxis > lcl_getChartAxis(const SchXMLAxis& rCurrentAxis, const Reference< chart::XDiagram >& rDiagram )
131 {
132 Reference< chart::XAxis > xAxis;
133 Reference< chart::XAxisSupplier > xAxisSuppl( rDiagram, uno::UNO_QUERY );
134 if( !xAxisSuppl.is() )
135 return xAxis;
136 if( rCurrentAxis.nAxisIndex == 0 )
137 xAxis = xAxisSuppl->getAxis(rCurrentAxis.eDimension);
138 else
139 xAxis = xAxisSuppl->getSecondaryAxis(rCurrentAxis.eDimension);
140 return xAxis;
141 }
142
143 /* returns a shape for the current axis's title. The property
144 "Has...AxisTitle" is set to "True" to get the shape
145 */
getTitleShape() const146 Reference< drawing::XShape > SchXMLAxisContext::getTitleShape() const
147 {
148 Reference< drawing::XShape > xResult;
149 Reference< beans::XPropertySet > xDiaProp( m_rImportHelper.GetChartDocument()->getDiagram(), uno::UNO_QUERY );
150 Reference< chart::XAxis > xAxis( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ) );
151 if( !xDiaProp.is() || !xAxis.is() )
152 return xResult;
153
154 OUString aPropName;
155 switch( m_aCurrentAxis.eDimension )
156 {
157 case SCH_XML_AXIS_X:
158 if( m_aCurrentAxis.nAxisIndex == 0 )
159 aPropName = "HasXAxisTitle";
160 else
161 aPropName = "HasSecondaryXAxisTitle";
162 break;
163 case SCH_XML_AXIS_Y:
164 if( m_aCurrentAxis.nAxisIndex == 0 )
165 aPropName = "HasYAxisTitle";
166 else
167 aPropName = "HasSecondaryYAxisTitle";
168 break;
169 case SCH_XML_AXIS_Z:
170 aPropName = "HasZAxisTitle";
171 break;
172 case SCH_XML_AXIS_UNDEF:
173 SAL_INFO("xmloff.chart", "Invalid axis" );
174 break;
175 }
176 xDiaProp->setPropertyValue( aPropName, uno::makeAny(true) );
177 xResult.set( xAxis->getAxisTitle(), uno::UNO_QUERY );
178 return xResult;
179 }
180
CreateGrid(const OUString & sAutoStyleName,bool bIsMajor)181 void SchXMLAxisContext::CreateGrid( const OUString& sAutoStyleName, bool bIsMajor )
182 {
183 Reference< beans::XPropertySet > xDiaProp( m_rImportHelper.GetChartDocument()->getDiagram(), uno::UNO_QUERY );
184 Reference< chart::XAxis > xAxis( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ) );
185 if( !xDiaProp.is() || !xAxis.is() )
186 return;
187
188 OUString aPropName;
189 switch( m_aCurrentAxis.eDimension )
190 {
191 case SCH_XML_AXIS_X:
192 if( bIsMajor )
193 aPropName = "HasXAxisGrid";
194 else
195 aPropName = "HasXAxisHelpGrid";
196 break;
197 case SCH_XML_AXIS_Y:
198 if( bIsMajor )
199 aPropName = "HasYAxisGrid";
200 else
201 aPropName = "HasYAxisHelpGrid";
202 break;
203 case SCH_XML_AXIS_Z:
204 if( bIsMajor )
205 aPropName = "HasZAxisGrid";
206 else
207 aPropName = "HasZAxisHelpGrid";
208 break;
209 case SCH_XML_AXIS_UNDEF:
210 SAL_INFO("xmloff.chart", "Invalid axis" );
211 break;
212 }
213 xDiaProp->setPropertyValue( aPropName, uno::makeAny(true) );
214
215 Reference< beans::XPropertySet > xGridProp;
216 if( bIsMajor )
217 xGridProp = xAxis->getMajorGrid();
218 else
219 xGridProp = xAxis->getMinorGrid();
220
221 // set properties
222 if( xGridProp.is())
223 {
224 // the line color is black as default, in the model it is a light gray
225 xGridProp->setPropertyValue("LineColor",
226 uno::makeAny( COL_BLACK ));
227 if (!sAutoStyleName.isEmpty())
228 m_rImportHelper.FillAutoStyle(sAutoStyleName, xGridProp);
229 }
230 }
231
startFastElement(sal_Int32,const css::uno::Reference<css::xml::sax::XFastAttributeList> & xAttrList)232 void SchXMLAxisContext::startFastElement( sal_Int32 /*nElement*/,
233 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
234 {
235 // parse attributes
236 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
237 {
238 switch(aIter.getToken())
239 {
240 case XML_ELEMENT(CHART, XML_DIMENSION):
241 {
242 SchXMLAxisDimension nEnumVal;
243 if( SvXMLUnitConverter::convertEnum( nEnumVal, aIter.toView(), aXMLAxisDimensionMap ))
244 m_aCurrentAxis.eDimension = nEnumVal;
245 }
246 break;
247 case XML_ELEMENT(CHART, XML_NAME):
248 m_aCurrentAxis.aName = aIter.toString();
249 break;
250 case XML_ELEMENT(CHART, XML_AXIS_TYPE):
251 case XML_ELEMENT(CHART_EXT, XML_AXIS_TYPE):
252 sal_uInt16 nEnumVal;
253 if( SvXMLUnitConverter::convertEnum( nEnumVal, aIter.toView(), aXMLAxisTypeMap ))
254 {
255 m_nAxisType = nEnumVal;
256 m_bAxisTypeImported = true;
257 }
258 break;
259 case XML_ELEMENT(CHART, XML_STYLE_NAME):
260 m_aAutoStyleName = aIter.toString();
261 break;
262 default:
263 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
264 }
265 }
266
267 // check for number of axes with same dimension
268 m_aCurrentAxis.nAxisIndex = 0;
269 sal_Int32 nNumOfAxes = m_rAxes.size();
270 for( sal_Int32 nCurrent = 0; nCurrent < nNumOfAxes; nCurrent++ )
271 {
272 if( m_rAxes[ nCurrent ].eDimension == m_aCurrentAxis.eDimension )
273 m_aCurrentAxis.nAxisIndex++;
274 }
275 CreateAxis();
276 }
277 namespace
278 {
279
lcl_getAxis(const Reference<frame::XModel> & xChartModel,sal_Int32 nDimensionIndex,sal_Int32 nAxisIndex)280 Reference< chart2::XAxis > lcl_getAxis( const Reference< frame::XModel >& xChartModel,
281 sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex )
282 {
283 Reference< chart2::XAxis > xAxis;
284
285 try
286 {
287 Reference< chart2::XChartDocument > xChart2Document( xChartModel, uno::UNO_QUERY );
288 if( xChart2Document.is() )
289 {
290 Reference< chart2::XDiagram > xDiagram( xChart2Document->getFirstDiagram());
291 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY_THROW );
292 uno::Sequence< Reference< chart2::XCoordinateSystem > >
293 aCooSysSeq( xCooSysCnt->getCoordinateSystems());
294 sal_Int32 nCooSysIndex = 0;
295 if( nCooSysIndex < aCooSysSeq.getLength() )
296 {
297 Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[nCooSysIndex] );
298 if( xCooSys.is() && nDimensionIndex < xCooSys->getDimension() )
299 {
300 const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex);
301 if( nAxisIndex <= nMaxAxisIndex )
302 xAxis = xCooSys->getAxisByDimension( nDimensionIndex, nAxisIndex );
303 }
304 }
305 }
306 }
307 catch( uno::Exception & )
308 {
309 SAL_INFO("xmloff.chart", "Couldn't get axis" );
310 }
311
312 return xAxis;
313 }
314
lcl_divideBy100(uno::Any & rDoubleAny)315 bool lcl_divideBy100( uno::Any& rDoubleAny )
316 {
317 bool bChanged = false;
318 double fValue=0.0;
319 if( (rDoubleAny>>=fValue) && (fValue!=0.0) )
320 {
321 fValue/=100.0;
322 rDoubleAny <<= fValue;
323 bChanged = true;
324 }
325 return bChanged;
326 }
327
lcl_AdaptWrongPercentScaleValues(chart2::ScaleData & rScaleData)328 bool lcl_AdaptWrongPercentScaleValues(chart2::ScaleData& rScaleData)
329 {
330 bool bChanged = lcl_divideBy100( rScaleData.Minimum );
331 bChanged = lcl_divideBy100( rScaleData.Maximum ) || bChanged;
332 bChanged = lcl_divideBy100( rScaleData.Origin ) || bChanged;
333 bChanged = lcl_divideBy100( rScaleData.IncrementData.Distance ) || bChanged;
334 return bChanged;
335 }
336
337 }//end anonymous namespace
338
CreateAxis()339 void SchXMLAxisContext::CreateAxis()
340 {
341 m_rAxes.push_back( m_aCurrentAxis );
342
343 Reference< beans::XPropertySet > xDiaProp( m_rImportHelper.GetChartDocument()->getDiagram(), uno::UNO_QUERY );
344 if( !xDiaProp.is() )
345 return;
346 OUString aPropName;
347 switch( m_aCurrentAxis.eDimension )
348 {
349 case SCH_XML_AXIS_X:
350 if( m_aCurrentAxis.nAxisIndex == 0 )
351 aPropName = "HasXAxis";
352 else
353 aPropName = "HasSecondaryXAxis";
354 break;
355 case SCH_XML_AXIS_Y:
356 if( m_aCurrentAxis.nAxisIndex == 0 )
357 aPropName = "HasYAxis";
358 else
359 aPropName = "HasSecondaryYAxis";
360 break;
361 case SCH_XML_AXIS_Z:
362 if( m_aCurrentAxis.nAxisIndex == 0 )
363 aPropName = "HasZAxis";
364 break;
365 case SCH_XML_AXIS_UNDEF:
366 SAL_INFO("xmloff.chart", "Invalid axis" );
367 break;
368 }
369 try
370 {
371 xDiaProp->setPropertyValue( aPropName, uno::makeAny(true) );
372 }
373 catch( beans::UnknownPropertyException & )
374 {
375 SAL_INFO("xmloff.chart", "Couldn't turn on axis" );
376 }
377 if( m_aCurrentAxis.eDimension==SCH_XML_AXIS_Z )
378 {
379 bool bSettingZAxisSucceeded = false;
380 try
381 {
382 xDiaProp->getPropertyValue( aPropName ) >>= bSettingZAxisSucceeded;
383 }
384 catch( beans::UnknownPropertyException & )
385 {
386 SAL_INFO("xmloff.chart", "Couldn't turn on z axis" );
387 }
388 if( !bSettingZAxisSucceeded )
389 return;
390 }
391
392 m_xAxisProps.set( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ), uno::UNO_QUERY );
393
394 if( m_bAddMissingXAxisForNetCharts && m_aCurrentAxis.eDimension==SCH_XML_AXIS_Y && m_aCurrentAxis.nAxisIndex==0 )
395 {
396 try
397 {
398 xDiaProp->setPropertyValue("HasXAxis", uno::makeAny(true) );
399 }
400 catch( beans::UnknownPropertyException & )
401 {
402 SAL_INFO("xmloff.chart", "Couldn't turn on x axis" );
403 }
404 }
405
406 // set properties
407 if( !m_xAxisProps.is())
408 return;
409
410 uno::Any aTrueBool( uno::makeAny( true ));
411 uno::Any aFalseBool( uno::makeAny( false ));
412
413 // #i109879# the line color is black as default, in the model it is a light gray
414 m_xAxisProps->setPropertyValue("LineColor",
415 uno::makeAny( COL_BLACK ));
416
417 m_xAxisProps->setPropertyValue("DisplayLabels", aFalseBool );
418
419 // Compatibility option: starting from LibreOffice 5.1 the rotated
420 // layout is preferred to staggering for axis labels.
421 // So the import default value for having compatibility with ODF
422 // documents created with earlier LibreOffice versions is `true`.
423 if( GetImport().getGeneratorVersion() != SvXMLImport::ProductVersionUnknown )
424 m_xAxisProps->setPropertyValue("TryStaggeringFirst", aTrueBool );
425
426 // #88077# AutoOrigin 'on' is default
427 m_xAxisProps->setPropertyValue("AutoOrigin", aTrueBool );
428
429 if( m_bAxisTypeImported )
430 m_xAxisProps->setPropertyValue("AxisType", uno::makeAny(m_nAxisType) );
431
432 if( !m_aAutoStyleName.isEmpty())
433 {
434 const SvXMLStylesContext* pStylesCtxt = m_rImportHelper.GetAutoStylesContext();
435 if (pStylesCtxt)
436 {
437 SvXMLStyleContext* pStyle = const_cast<SvXMLStyleContext*>(pStylesCtxt->FindStyleChildContext(SchXMLImportHelper::GetChartFamilyID(), m_aAutoStyleName));
438
439 if (XMLPropStyleContext * pPropStyleContext = dynamic_cast<XMLPropStyleContext*>(pStyle))
440 {
441 pPropStyleContext->FillPropertySet(m_xAxisProps);
442
443 if( m_bAdaptWrongPercentScaleValues && m_aCurrentAxis.eDimension==SCH_XML_AXIS_Y )
444 {
445 //set scale data of added x axis back to default
446 Reference< chart2::XAxis > xAxis( lcl_getAxis( GetImport().GetModel(),
447 m_aCurrentAxis.eDimension, m_aCurrentAxis.nAxisIndex ) );
448 if( xAxis.is() )
449 {
450 chart2::ScaleData aScaleData( xAxis->getScaleData());
451 if( lcl_AdaptWrongPercentScaleValues(aScaleData) )
452 xAxis->setScaleData( aScaleData );
453 }
454 }
455
456 if( m_bAddMissingXAxisForNetCharts )
457 {
458 //copy style from y axis to added x axis:
459
460 Reference< chart::XAxisSupplier > xAxisSuppl( xDiaProp, uno::UNO_QUERY );
461 if( xAxisSuppl.is() )
462 {
463 Reference< beans::XPropertySet > xXAxisProp( xAxisSuppl->getAxis(0), uno::UNO_QUERY );
464 pPropStyleContext->FillPropertySet(xXAxisProp);
465 }
466
467 //set scale data of added x axis back to default
468 Reference< chart2::XAxis > xAxis( lcl_getAxis( GetImport().GetModel(),
469 0 /*nDimensionIndex*/, 0 /*nAxisIndex*/ ) );
470 if( xAxis.is() )
471 {
472 chart2::ScaleData aScaleData;
473 aScaleData.AxisType = chart2::AxisType::CATEGORY;
474 aScaleData.Orientation = chart2::AxisOrientation_MATHEMATICAL;
475 xAxis->setScaleData( aScaleData );
476 }
477
478 //set line style of added x axis to invisible
479 Reference< beans::XPropertySet > xNewAxisProp( xAxis, uno::UNO_QUERY );
480 if( xNewAxisProp.is() )
481 {
482 xNewAxisProp->setPropertyValue("LineStyle"
483 , uno::makeAny(drawing::LineStyle_NONE));
484 }
485 }
486
487 if( m_bAdaptXAxisOrientationForOld2DBarCharts && m_aCurrentAxis.eDimension == SCH_XML_AXIS_X )
488 {
489 bool bIs3DChart = false;
490 if( xDiaProp.is() && ( xDiaProp->getPropertyValue("Dim3D") >>= bIs3DChart )
491 && !bIs3DChart )
492 {
493 Reference< chart2::XChartDocument > xChart2Document( GetImport().GetModel(), uno::UNO_QUERY );
494 if( xChart2Document.is() )
495 {
496 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xChart2Document->getFirstDiagram(), uno::UNO_QUERY );
497 if( xCooSysCnt.is() )
498 {
499 uno::Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems() );
500 if( aCooSysSeq.hasElements() )
501 {
502 bool bSwapXandYAxis = false;
503 Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[0] );
504 Reference< beans::XPropertySet > xCooSysProp( xCooSys, uno::UNO_QUERY );
505 if( xCooSysProp.is() && ( xCooSysProp->getPropertyValue("SwapXAndYAxis") >>= bSwapXandYAxis )
506 && bSwapXandYAxis )
507 {
508 Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension( 0, m_aCurrentAxis.nAxisIndex );
509 if( xAxis.is() )
510 {
511 chart2::ScaleData aScaleData = xAxis->getScaleData();
512 aScaleData.Orientation = chart2::AxisOrientation_REVERSE;
513 xAxis->setScaleData( aScaleData );
514 }
515 }
516 }
517 }
518 }
519 }
520 }
521
522 m_rbAxisPositionAttributeImported = m_rbAxisPositionAttributeImported || SchXMLTools::getPropertyFromContext(
523 u"CrossoverPosition", pPropStyleContext, pStylesCtxt ).hasValue();
524 }
525 }
526 }
527
528 if (m_aCurrentAxis.eDimension != SCH_XML_AXIS_X)
529 return;
530
531 Reference<chart2::XAxis> xAxis(lcl_getAxis(GetImport().GetModel(), m_aCurrentAxis.eDimension, m_aCurrentAxis.nAxisIndex));
532 if (!xAxis.is())
533 return;
534
535 chart2::ScaleData aScaleData(xAxis->getScaleData());
536 bool bIs3DChart = false;
537 double fMajorOrigin = -1;
538 OUString sChartType = m_xDiagram->getDiagramType();
539 if ((xDiaProp->getPropertyValue("Dim3D") >>= bIs3DChart) && bIs3DChart
540 && (sChartType == "com.sun.star.chart.BarDiagram" || sChartType == "com.sun.star.chart.StockDiagram"))
541 {
542 aScaleData.ShiftedCategoryPosition = true;
543 xAxis->setScaleData(aScaleData);
544 }
545 else if ((m_xAxisProps->getPropertyValue("MajorOrigin") >>= fMajorOrigin)
546 && (rtl::math::approxEqual(fMajorOrigin, 0.0) || rtl::math::approxEqual(fMajorOrigin, 0.5)))
547 {
548 aScaleData.ShiftedCategoryPosition = rtl::math::approxEqual(fMajorOrigin, 0.5);
549 xAxis->setScaleData(aScaleData);
550 }
551 }
552
SetAxisTitle()553 void SchXMLAxisContext::SetAxisTitle()
554 {
555 if( m_aCurrentAxis.aTitle.isEmpty() )
556 return;
557
558 Reference< chart::XAxis > xAxis( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ) );
559 if( !xAxis.is() )
560 return;
561
562 Reference< beans::XPropertySet > xTitleProp( xAxis->getAxisTitle() );
563 if( xTitleProp.is() )
564 {
565 try
566 {
567 xTitleProp->setPropertyValue("String", uno::makeAny(m_aCurrentAxis.aTitle) );
568 }
569 catch( beans::UnknownPropertyException & )
570 {
571 SAL_INFO("xmloff.chart", "Property String for Title not available" );
572 }
573 }
574 }
575
createFastChildContext(sal_Int32 nElement,const css::uno::Reference<css::xml::sax::XFastAttributeList> & xAttrList)576 css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLAxisContext::createFastChildContext(
577 sal_Int32 nElement,
578 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
579 {
580 switch( nElement )
581 {
582 case XML_ELEMENT(CHART, XML_TITLE):
583 {
584 Reference< drawing::XShape > xTitleShape = getTitleShape();
585 return new SchXMLTitleContext( m_rImportHelper, GetImport(),
586 m_aCurrentAxis.aTitle,
587 xTitleShape );
588 }
589 break;
590
591 case XML_ELEMENT(CHART, XML_CATEGORIES):
592 m_aCurrentAxis.bHasCategories = true;
593 return new SchXMLCategoriesContext( GetImport(),
594 m_rCategoriesAddress );
595 break;
596
597 case XML_ELEMENT(CHART, XML_DATE_SCALE):
598 case XML_ELEMENT(CHART_EXT, XML_DATE_SCALE):
599 m_bDateScaleImported = true;
600 return new DateScaleContext( GetImport(), m_xAxisProps );
601
602 case XML_ELEMENT(CHART, XML_GRID):
603 {
604 bool bIsMajor = true; // default value for class is "major"
605 OUString sAutoStyleName;
606
607 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
608 {
609 switch (aIter.getToken())
610 {
611 case XML_ELEMENT(CHART, XML_CLASS):
612 if( IsXMLToken( aIter, XML_MINOR ) )
613 bIsMajor = false;
614 break;
615 case XML_ELEMENT(CHART, XML_STYLE_NAME):
616 sAutoStyleName = aIter.toString();
617 break;
618 default:
619 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
620 }
621 }
622
623 CreateGrid( sAutoStyleName, bIsMajor );
624
625 // don't create a context => use default context. grid elements are empty
626 }
627 break;
628
629 default:
630 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
631 break;
632 }
633
634 return nullptr;
635 }
636
endFastElement(sal_Int32)637 void SchXMLAxisContext::endFastElement(sal_Int32 )
638 {
639 if( !m_bDateScaleImported && m_nAxisType==chart::ChartAxisType::AUTOMATIC )
640 {
641 Reference< chart2::XAxis > xAxis( lcl_getAxis( GetImport().GetModel(), m_aCurrentAxis.eDimension, m_aCurrentAxis.nAxisIndex ) );
642 if( xAxis.is() )
643 {
644 chart2::ScaleData aScaleData( xAxis->getScaleData());
645 aScaleData.AutoDateAxis = false;//different default for older documents
646 xAxis->setScaleData( aScaleData );
647 }
648 }
649
650 SetAxisTitle();
651 }
652
653 namespace
654 {
655
lcl_getAxis(const Reference<chart2::XCoordinateSystem> & rCooSys,sal_Int32 nDimensionIndex,sal_Int32 nAxisIndex)656 Reference< chart2::XAxis > lcl_getAxis( const Reference< chart2::XCoordinateSystem >& rCooSys, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex )
657 {
658 Reference< chart2::XAxis > xAxis;
659 try
660 {
661 xAxis = rCooSys->getAxisByDimension( nDimensionIndex, nAxisIndex );
662 }
663 catch( uno::Exception & )
664 {
665 }
666 return xAxis;
667 }
668
669 } // anonymous namespace
670
CorrectAxisPositions(const Reference<chart2::XChartDocument> & xNewDoc,std::u16string_view rChartTypeServiceName,std::u16string_view rODFVersionOfFile,bool bAxisPositionAttributeImported)671 void SchXMLAxisContext::CorrectAxisPositions( const Reference< chart2::XChartDocument >& xNewDoc,
672 std::u16string_view rChartTypeServiceName,
673 std::u16string_view rODFVersionOfFile,
674 bool bAxisPositionAttributeImported )
675 {
676 if( !(rODFVersionOfFile.empty() || rODFVersionOfFile == u"1.0" || rODFVersionOfFile == u"1.1"
677 || ( rODFVersionOfFile == u"1.2" && !bAxisPositionAttributeImported )) )
678 return;
679
680 try
681 {
682 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xNewDoc->getFirstDiagram(), uno::UNO_QUERY_THROW );
683 uno::Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems());
684 if( aCooSysSeq.hasElements() )
685 {
686 Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[0] );
687 if( xCooSys.is() )
688 {
689 Reference< chart2::XAxis > xMainXAxis = lcl_getAxis( xCooSys, 0, 0 );
690 Reference< chart2::XAxis > xMainYAxis = lcl_getAxis( xCooSys, 1, 0 );
691 //Reference< chart2::XAxis > xMajorZAxis = lcl_getAxis( xCooSys, 2, 0 );
692 Reference< chart2::XAxis > xSecondaryXAxis = lcl_getAxis( xCooSys, 0, 1 );
693 Reference< chart2::XAxis > xSecondaryYAxis = lcl_getAxis( xCooSys, 1, 1 );
694
695 Reference< beans::XPropertySet > xMainXAxisProp( xMainXAxis, uno::UNO_QUERY );
696 Reference< beans::XPropertySet > xMainYAxisProp( xMainYAxis, uno::UNO_QUERY );
697 Reference< beans::XPropertySet > xSecondaryXAxisProp( xSecondaryXAxis, uno::UNO_QUERY );
698 Reference< beans::XPropertySet > xSecondaryYAxisProp( xSecondaryYAxis, uno::UNO_QUERY );
699
700 if( xMainXAxisProp.is() && xMainYAxisProp.is() )
701 {
702 chart2::ScaleData aMainXScale = xMainXAxis->getScaleData();
703 if( rChartTypeServiceName == u"com.sun.star.chart2.ScatterChartType" )
704 {
705 xMainYAxisProp->setPropertyValue("CrossoverPosition"
706 , uno::makeAny( css::chart::ChartAxisPosition_VALUE) );
707 double fCrossoverValue = 0.0;
708 aMainXScale.Origin >>= fCrossoverValue;
709 xMainYAxisProp->setPropertyValue("CrossoverValue"
710 , uno::makeAny( fCrossoverValue ) );
711
712 if( aMainXScale.Orientation == chart2::AxisOrientation_REVERSE )
713 {
714 xMainYAxisProp->setPropertyValue("LabelPosition"
715 , uno::makeAny( css::chart::ChartAxisLabelPosition_OUTSIDE_END) );
716 xMainYAxisProp->setPropertyValue("MarkPosition"
717 , uno::makeAny( css::chart::ChartAxisMarkPosition_AT_LABELS) );
718 if( xSecondaryYAxisProp.is() )
719 xSecondaryYAxisProp->setPropertyValue("CrossoverPosition"
720 , uno::makeAny( css::chart::ChartAxisPosition_START) );
721 }
722 else
723 {
724 xMainYAxisProp->setPropertyValue("LabelPosition"
725 , uno::makeAny( css::chart::ChartAxisLabelPosition_OUTSIDE_START) );
726 xMainYAxisProp->setPropertyValue("MarkPosition"
727 , uno::makeAny( css::chart::ChartAxisMarkPosition_AT_LABELS) );
728 if( xSecondaryYAxisProp.is() )
729 xSecondaryYAxisProp->setPropertyValue("CrossoverPosition"
730 , uno::makeAny( css::chart::ChartAxisPosition_END) );
731 }
732 }
733 else
734 {
735 if( aMainXScale.Orientation == chart2::AxisOrientation_REVERSE )
736 {
737 xMainYAxisProp->setPropertyValue("CrossoverPosition"
738 , uno::makeAny( css::chart::ChartAxisPosition_END) );
739 if( xSecondaryYAxisProp.is() )
740 xSecondaryYAxisProp->setPropertyValue("CrossoverPosition"
741 , uno::makeAny( css::chart::ChartAxisPosition_START) );
742 }
743 else
744 {
745 xMainYAxisProp->setPropertyValue("CrossoverPosition"
746 , uno::makeAny( css::chart::ChartAxisPosition_START) );
747 if( xSecondaryYAxisProp.is() )
748 xSecondaryYAxisProp->setPropertyValue("CrossoverPosition"
749 , uno::makeAny( css::chart::ChartAxisPosition_END) );
750 }
751 }
752
753 chart2::ScaleData aMainYScale = xMainYAxis->getScaleData();
754 xMainXAxisProp->setPropertyValue("CrossoverPosition"
755 , uno::makeAny( css::chart::ChartAxisPosition_VALUE) );
756 double fCrossoverValue = 0.0;
757 aMainYScale.Origin >>= fCrossoverValue;
758 xMainXAxisProp->setPropertyValue("CrossoverValue"
759 , uno::makeAny( fCrossoverValue ) );
760
761 if( aMainYScale.Orientation == chart2::AxisOrientation_REVERSE )
762 {
763 xMainXAxisProp->setPropertyValue("LabelPosition"
764 , uno::makeAny( css::chart::ChartAxisLabelPosition_OUTSIDE_END) );
765 xMainXAxisProp->setPropertyValue("MarkPosition"
766 , uno::makeAny( css::chart::ChartAxisMarkPosition_AT_LABELS) );
767 if( xSecondaryXAxisProp.is() )
768 xSecondaryXAxisProp->setPropertyValue("CrossoverPosition"
769 , uno::makeAny( css::chart::ChartAxisPosition_START) );
770 }
771 else
772 {
773 xMainXAxisProp->setPropertyValue("LabelPosition"
774 , uno::makeAny( css::chart::ChartAxisLabelPosition_OUTSIDE_START) );
775 xMainXAxisProp->setPropertyValue("MarkPosition"
776 , uno::makeAny( css::chart::ChartAxisMarkPosition_AT_LABELS) );
777 if( xSecondaryXAxisProp.is() )
778 xSecondaryXAxisProp->setPropertyValue("CrossoverPosition"
779 , uno::makeAny( css::chart::ChartAxisPosition_END) );
780 }
781 }
782 }
783 }
784 }
785 catch( uno::Exception & )
786 {
787 }
788 }
789
SchXMLCategoriesContext(SvXMLImport & rImport,OUString & rAddress)790 SchXMLCategoriesContext::SchXMLCategoriesContext(
791 SvXMLImport& rImport,
792 OUString& rAddress ) :
793 SvXMLImportContext( rImport ),
794 mrAddress( rAddress )
795 {
796 }
797
startFastElement(sal_Int32,const css::uno::Reference<css::xml::sax::XFastAttributeList> & xAttrList)798 void SchXMLCategoriesContext::startFastElement( sal_Int32 /*nElement*/,
799 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
800 {
801 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
802 {
803 if( aIter.getToken() == XML_ELEMENT(TABLE, XML_CELL_RANGE_ADDRESS) )
804 mrAddress = aIter.toString();
805 else
806 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
807 }
808 }
809
DateScaleContext(SvXMLImport & rImport,const Reference<beans::XPropertySet> & rAxisProps)810 DateScaleContext::DateScaleContext(
811 SvXMLImport& rImport,
812 const Reference< beans::XPropertySet >& rAxisProps ) :
813 SvXMLImportContext( rImport ),
814 m_xAxisProps( rAxisProps )
815 {
816 }
817
818 namespace
819 {
lcl_getTimeUnit(const sax_fastparser::FastAttributeList::FastAttributeIter & rValue)820 sal_Int32 lcl_getTimeUnit( const sax_fastparser::FastAttributeList::FastAttributeIter& rValue )
821 {
822 sal_Int32 nTimeUnit = css::chart::TimeUnit::DAY;
823 if( IsXMLToken( rValue, XML_DAYS ) )
824 nTimeUnit = css::chart::TimeUnit::DAY;
825 else if( IsXMLToken( rValue, XML_MONTHS ) )
826 nTimeUnit = css::chart::TimeUnit::MONTH;
827 else if( IsXMLToken( rValue, XML_YEARS ) )
828 nTimeUnit = css::chart::TimeUnit::YEAR;
829 return nTimeUnit;
830 }
831
832 }
833
startFastElement(sal_Int32,const css::uno::Reference<css::xml::sax::XFastAttributeList> & xAttrList)834 void DateScaleContext::startFastElement( sal_Int32 /*nElement*/,
835 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
836 {
837 if( !m_xAxisProps.is() )
838 return;
839
840 // parse attributes
841 bool bSetNewIncrement=false;
842 chart::TimeIncrement aIncrement;
843 m_xAxisProps->getPropertyValue("TimeIncrement") >>= aIncrement;
844
845 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
846 {
847 switch( aIter.getToken() )
848 {
849 case XML_ELEMENT(CHART, XML_BASE_TIME_UNIT):
850 {
851 aIncrement.TimeResolution <<= lcl_getTimeUnit(aIter);
852 bSetNewIncrement = true;
853 }
854 break;
855 case XML_ELEMENT(CHART, XML_MAJOR_INTERVAL_VALUE):
856 {
857 chart::TimeInterval aInterval(1,0);
858 aIncrement.MajorTimeInterval >>= aInterval;
859 ::sax::Converter::convertNumber( aInterval.Number, aIter.toView() );
860 aIncrement.MajorTimeInterval <<= aInterval;
861 bSetNewIncrement = true;
862 }
863 break;
864 case XML_ELEMENT(CHART, XML_MAJOR_INTERVAL_UNIT):
865 {
866 chart::TimeInterval aInterval(1,0);
867 aIncrement.MajorTimeInterval >>= aInterval;
868 aInterval.TimeUnit = lcl_getTimeUnit(aIter);
869 aIncrement.MajorTimeInterval <<= aInterval;
870 bSetNewIncrement = true;
871 }
872 break;
873 case XML_ELEMENT(CHART, XML_MINOR_INTERVAL_VALUE):
874 {
875 chart::TimeInterval aInterval(1,0);
876 aIncrement.MinorTimeInterval >>= aInterval;
877 ::sax::Converter::convertNumber( aInterval.Number, aIter.toView() );
878 aIncrement.MinorTimeInterval <<= aInterval;
879 bSetNewIncrement = true;
880 }
881 break;
882 case XML_ELEMENT(CHART, XML_MINOR_INTERVAL_UNIT):
883 {
884 chart::TimeInterval aInterval(1,0);
885 aIncrement.MinorTimeInterval >>= aInterval;
886 aInterval.TimeUnit = lcl_getTimeUnit(aIter);
887 aIncrement.MinorTimeInterval <<= aInterval;
888 bSetNewIncrement = true;
889 }
890 break;
891 default:
892 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
893 }
894 }
895
896 if( bSetNewIncrement )
897 m_xAxisProps->setPropertyValue("TimeIncrement", uno::makeAny( aIncrement ) );
898 }
899
900 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
901