LibreOffice Module sc (master) 1
xichart.cxx
Go to the documentation of this file.
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 <xichart.hxx>
21
22#include <algorithm>
23#include <memory>
24#include <utility>
25
26#include <com/sun/star/frame/XModel.hpp>
27#include <com/sun/star/drawing/Direction3D.hpp>
28#include <com/sun/star/drawing/ProjectionMode.hpp>
29#include <com/sun/star/drawing/ShadeMode.hpp>
30#include <com/sun/star/drawing/XShape.hpp>
31#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
32#include <com/sun/star/chart/ChartAxisArrangeOrderType.hpp>
33#include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
34#include <com/sun/star/chart/ChartAxisMarkPosition.hpp>
35#include <com/sun/star/chart/ChartAxisPosition.hpp>
36#include <com/sun/star/chart/ChartLegendExpansion.hpp>
37#include <com/sun/star/chart/TimeInterval.hpp>
38#include <com/sun/star/chart/TimeUnit.hpp>
39#include <com/sun/star/chart/XChartDocument.hpp>
40#include <com/sun/star/chart/XDiagramPositioning.hpp>
41#include <com/sun/star/chart/DataLabelPlacement.hpp>
42#include <com/sun/star/chart/ErrorBarStyle.hpp>
43#include <com/sun/star/chart/MissingValueTreatment.hpp>
44#include <com/sun/star/chart2/LinearRegressionCurve.hpp>
45#include <com/sun/star/chart2/ExponentialRegressionCurve.hpp>
46#include <com/sun/star/chart2/LogarithmicRegressionCurve.hpp>
47#include <com/sun/star/chart2/PotentialRegressionCurve.hpp>
48#include <com/sun/star/chart2/PolynomialRegressionCurve.hpp>
49#include <com/sun/star/chart2/MovingAverageRegressionCurve.hpp>
50#include <com/sun/star/chart2/CartesianCoordinateSystem2d.hpp>
51#include <com/sun/star/chart2/CartesianCoordinateSystem3d.hpp>
52#include <com/sun/star/chart2/FormattedString.hpp>
53#include <com/sun/star/chart2/LogarithmicScaling.hpp>
54#include <com/sun/star/chart2/LinearScaling.hpp>
55#include <com/sun/star/chart2/PolarCoordinateSystem2d.hpp>
56#include <com/sun/star/chart2/PolarCoordinateSystem3d.hpp>
57#include <com/sun/star/chart2/XChartDocument.hpp>
58#include <com/sun/star/chart2/XDiagram.hpp>
59#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
60#include <com/sun/star/chart2/XChartTypeContainer.hpp>
61#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
62#include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
63#include <com/sun/star/chart2/XTitled.hpp>
64#include <com/sun/star/chart2/AxisType.hpp>
65#include <com/sun/star/chart2/CurveStyle.hpp>
66#include <com/sun/star/chart2/DataPointGeometry3D.hpp>
67#include <com/sun/star/chart2/DataPointLabel.hpp>
68#include <com/sun/star/chart2/LegendPosition.hpp>
69#include <com/sun/star/chart2/StackingDirection.hpp>
70#include <com/sun/star/chart2/TickmarkStyle.hpp>
71#include <com/sun/star/chart2/RelativePosition.hpp>
72#include <com/sun/star/chart2/RelativeSize.hpp>
73#include <com/sun/star/chart2/data/XDataProvider.hpp>
74#include <com/sun/star/chart2/data/XDataReceiver.hpp>
75#include <com/sun/star/chart2/data/XDataSink.hpp>
76#include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
78#include <o3tl/numeric.hxx>
80#include <sfx2/objsh.hxx>
81#include <svx/svdpage.hxx>
82#include <svx/unoapi.hxx>
83#include <sal/log.hxx>
84#include <tools/helpers.hxx>
85
86#include <document.hxx>
87#include <drwlayer.hxx>
88#include <tokenarray.hxx>
89#include <compiler.hxx>
90#include <reftokenhelper.hxx>
91#include <chartlis.hxx>
92#include <globstr.hrc>
93#include <scresid.hxx>
94#include <xltracer.hxx>
95#include <xltools.hxx>
96#include <xistream.hxx>
97#include <xiformula.hxx>
98#include <xistyle.hxx>
99#include <xipage.hxx>
100#include <xiview.hxx>
101
102using ::com::sun::star::uno::Any;
103using ::com::sun::star::uno::Reference;
104using ::com::sun::star::uno::Sequence;
105using ::com::sun::star::uno::UNO_QUERY;
106using ::com::sun::star::uno::UNO_QUERY_THROW;
107using ::com::sun::star::uno::UNO_SET_THROW;
108using ::com::sun::star::uno::Exception;
109using ::com::sun::star::beans::XPropertySet;
110using ::com::sun::star::frame::XModel;
111using ::com::sun::star::util::XNumberFormatsSupplier;
112using ::com::sun::star::drawing::XDrawPage;
113using ::com::sun::star::drawing::XDrawPageSupplier;
114using ::com::sun::star::drawing::XShape;
115
116using namespace ::com::sun::star::chart2;
117
118using ::com::sun::star::chart2::data::XDataProvider;
119using ::com::sun::star::chart2::data::XDataReceiver;
120using ::com::sun::star::chart2::data::XDataSequence;
121using ::com::sun::star::chart2::data::XDataSink;
122using ::com::sun::star::chart2::data::XLabeledDataSequence;
123using ::com::sun::star::chart2::data::LabeledDataSequence;
124
125using ::formula::FormulaToken;
126using ::formula::FormulaTokenArrayPlainIterator;
127using ::std::unique_ptr;
128
129namespace cssc = ::com::sun::star::chart;
130namespace cssc2 = ::com::sun::star::chart2;
131
132// Helpers ====================================================================
133
134namespace {
135
137{
138 rRect.mnX = rStrm.ReadInt32();
139 rRect.mnY = rStrm.ReadInt32();
140 rRect.mnWidth = rStrm.ReadInt32();
141 rRect.mnHeight = rStrm.ReadInt32();
142 return rStrm;
143}
144
145void lclSetValueOrClearAny( Any& rAny, double fValue, bool bClear )
146{
147 if( bClear )
148 rAny.clear();
149 else
150 rAny <<= fValue;
151}
152
153void lclSetExpValueOrClearAny( Any& rAny, double fValue, bool bLogScale, bool bClear )
154{
155 if( !bClear && bLogScale )
156 fValue = pow( 10.0, fValue );
157 lclSetValueOrClearAny( rAny, fValue, bClear );
159
160double lclGetSerialDay( const XclImpRoot& rRoot, sal_uInt16 nValue, sal_uInt16 nTimeUnit )
161{
162 switch( nTimeUnit )
163 {
165 return nValue;
167 return rRoot.GetDoubleFromDateTime( Date( 1, static_cast< sal_uInt16 >( 1 + nValue % 12 ), static_cast< sal_uInt16 >( rRoot.GetBaseYear() + nValue / 12 ) ) );
169 return rRoot.GetDoubleFromDateTime( Date( 1, 1, static_cast< sal_uInt16 >( rRoot.GetBaseYear() + nValue ) ) );
170 default:
171 OSL_ENSURE( false, "lclGetSerialDay - unexpected time unit" );
172 }
173 return nValue;
174}
175
176void lclConvertTimeValue( const XclImpRoot& rRoot, Any& rAny, sal_uInt16 nValue, bool bAuto, sal_uInt16 nTimeUnit )
177{
178 if( bAuto )
179 rAny.clear();
180 else
181 rAny <<= lclGetSerialDay( rRoot, nValue, nTimeUnit );
182}
183
184sal_Int32 lclGetApiTimeUnit( sal_uInt16 nTimeUnit )
185{
186 switch( nTimeUnit )
187 {
188 case EXC_CHDATERANGE_DAYS: return cssc::TimeUnit::DAY;
189 case EXC_CHDATERANGE_MONTHS: return cssc::TimeUnit::MONTH;
190 case EXC_CHDATERANGE_YEARS: return cssc::TimeUnit::YEAR;
191 default: OSL_ENSURE( false, "lclGetApiTimeUnit - unexpected time unit" );
192 }
193 return cssc::TimeUnit::DAY;
194}
195
196void lclConvertTimeInterval( Any& rInterval, sal_uInt16 nValue, bool bAuto, sal_uInt16 nTimeUnit )
197{
198 if( bAuto || (nValue == 0) )
199 rInterval.clear();
200 else
201 rInterval <<= cssc::TimeInterval( nValue, lclGetApiTimeUnit( nTimeUnit ) );
202}
203
204} // namespace
205
206// Common =====================================================================
207
210{
212
213 explicit XclImpChRootData( XclImpChChart& rChartData ) : mrChartData( rChartData ) {}
214};
215
217 XclImpRoot( rRoot ),
218 mxChData( std::make_shared<XclImpChRootData>( rChartData ) )
219{
220}
221
223{
224}
225
227{
228 return mxChData->mrChartData;
229}
230
232{
233 return mxChData->mxTypeInfoProv->GetTypeInfo( eType );
234}
235
236const XclChTypeInfo& XclImpChRoot::GetChartTypeInfo( sal_uInt16 nRecId ) const
237{
238 return mxChData->mxTypeInfoProv->GetTypeInfoFromRecId( nRecId );
239}
240
242{
243 return mxChData->mxFmtInfoProv->GetFormatInfo( eObjType );
244}
245
247{
249}
250
251Color XclImpChRoot::GetSeriesLineAutoColor( sal_uInt16 nFormatIdx ) const
252{
254}
255
256Color XclImpChRoot::GetSeriesFillAutoColor( sal_uInt16 nFormatIdx ) const
257{
258 const XclImpPalette& rPal = GetPalette();
259 Color aColor = rPal.GetColor( XclChartHelper::GetSeriesFillAutoColorIdx( nFormatIdx ) );
261 return ScfTools::GetMixedColor( aColor, rPal.GetColor( EXC_COLOR_CHWINDOWBACK ), nTrans );
262}
263
264void XclImpChRoot::InitConversion( const Reference<XChartDocument>& xChartDoc, const tools::Rectangle& rChartRect ) const
265{
266 // create formatting object tables
267 mxChData->InitConversion( GetRoot(), xChartDoc, rChartRect );
268
269 // lock the model to suppress any internal updates
270 if( xChartDoc.is() )
271 xChartDoc->lockControllers();
272
273 SfxObjectShell* pDocShell = GetDocShell();
274 Reference< XDataReceiver > xDataRec( xChartDoc, UNO_QUERY );
275 if( pDocShell && xDataRec.is() )
276 {
277 // create and register a data provider
278 Reference< XDataProvider > xDataProv(
280 if( xDataProv.is() )
281 xDataRec->attachDataProvider( xDataProv );
282 // attach the number formatter
283 Reference< XNumberFormatsSupplier > xNumFmtSupp( pDocShell->GetModel(), UNO_QUERY );
284 if( xNumFmtSupp.is() )
285 xDataRec->attachNumberFormatsSupplier( xNumFmtSupp );
286 }
287}
288
290{
292 // unlock the model
293 Reference< XModel > xModel = mxChData->mxChartDoc;
294 if( xModel.is() )
295 xModel->unlockControllers();
297
298 mxChData->FinishConversion();
299}
300
301Reference< XDataProvider > XclImpChRoot::GetDataProvider() const
302{
303 return mxChData->mxChartDoc->getDataProvider();
304}
305
306Reference< XShape > XclImpChRoot::GetTitleShape( const XclChTextKey& rTitleKey ) const
307{
308 return mxChData->GetTitleShape( rTitleKey );
309}
310
311sal_Int32 XclImpChRoot::CalcHmmFromChartX( sal_Int32 nPosX ) const
312{
313 return static_cast< sal_Int32 >( mxChData->mfUnitSizeX * nPosX + mxChData->mnBorderGapX + 0.5 );
314}
315
316sal_Int32 XclImpChRoot::CalcHmmFromChartY( sal_Int32 nPosY ) const
317{
318 return static_cast< sal_Int32 >( mxChData->mfUnitSizeY * nPosY + mxChData->mnBorderGapY + 0.5 );
319}
320
321css::awt::Rectangle XclImpChRoot::CalcHmmFromChartRect( const XclChRectangle& rRect ) const
322{
323 return css::awt::Rectangle(
324 CalcHmmFromChartX( rRect.mnX ),
325 CalcHmmFromChartY( rRect.mnY ),
326 CalcHmmFromChartX( rRect.mnWidth ),
327 CalcHmmFromChartY( rRect.mnHeight ) );
328}
329
330double XclImpChRoot::CalcRelativeFromHmmX( sal_Int32 nPosX ) const
331{
332 const tools::Long nWidth = mxChData->maChartRect.GetWidth();
333 if (!nWidth)
334 throw o3tl::divide_by_zero();
335 return static_cast<double>(nPosX) / nWidth;
336}
337
338double XclImpChRoot::CalcRelativeFromHmmY( sal_Int32 nPosY ) const
339{
340 const tools::Long nHeight = mxChData->maChartRect.GetHeight();
341 if (!nHeight)
342 throw o3tl::divide_by_zero();
343 return static_cast<double >(nPosY) / nHeight;
344}
345
346double XclImpChRoot::CalcRelativeFromChartX( sal_Int32 nPosX ) const
347{
348 return CalcRelativeFromHmmX( CalcHmmFromChartX( nPosX ) );
349}
350
351double XclImpChRoot::CalcRelativeFromChartY( sal_Int32 nPosY ) const
352{
353 return CalcRelativeFromHmmY( CalcHmmFromChartY( nPosY ) );
354}
355
357 const XclChLineFormat& rLineFmt, XclChPropertyMode ePropMode ) const
358{
360 rPropSet, *mxChData->mxLineDashTable, rLineFmt, ePropMode );
361}
362
364 const XclChAreaFormat& rAreaFmt, XclChPropertyMode ePropMode ) const
365{
366 GetChartPropSetHelper().WriteAreaProperties( rPropSet, rAreaFmt, ePropMode );
367}
368
370 const XclChEscherFormat& rEscherFmt, const XclChPicFormat* pPicFmt,
371 sal_uInt32 nDffFillType, XclChPropertyMode ePropMode ) const
372{
374 *mxChData->mxGradientTable, *mxChData->mxBitmapTable,
375 rEscherFmt, pPicFmt, nDffFillType, ePropMode );
376}
377
379 sal_uInt16 nFontIdx, const Color* pFontColor ) const
380{
381 GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CHART, nFontIdx, pFontColor );
382}
383
384void XclImpChRoot::ConvertPieRotation( ScfPropertySet& rPropSet, sal_uInt16 nAngle )
385{
386 sal_Int32 nApiRot = (450 - (nAngle % 360)) % 360;
387 rPropSet.SetProperty( EXC_CHPROP_STARTINGANGLE, nApiRot );
388}
389
391{
392}
393
395{
396 // read contents of the header record
398
399 // only read sub records, if the next record is a CHBEGIN
400 if( rStrm.GetNextRecId() != EXC_ID_CHBEGIN )
401 return;
402
403 // read the CHBEGIN record, may be used for special initial processing
404 rStrm.StartNextRecord();
406
407 // read the nested records
408 bool bLoop = true;
409 while( bLoop && rStrm.StartNextRecord() )
410 {
411 sal_uInt16 nRecId = rStrm.GetRecId();
412 bLoop = nRecId != EXC_ID_CHEND;
413 // skip unsupported nested blocks
414 if( nRecId == EXC_ID_CHBEGIN )
415 SkipBlock( rStrm );
416 else
418 }
419 /* Returns with current CHEND record or unchanged stream, if no record
420 group present. In every case another call to StartNextRecord() will go
421 to next record of interest. */
422}
423
425{
426 OSL_ENSURE( rStrm.GetRecId() == EXC_ID_CHBEGIN, "XclImpChGroupBase::SkipBlock - no CHBEGIN record" );
427 // do nothing if current record is not CHBEGIN
428 bool bLoop = rStrm.GetRecId() == EXC_ID_CHBEGIN;
429 while( bLoop && rStrm.StartNextRecord() )
430 {
431 sal_uInt16 nRecId = rStrm.GetRecId();
432 bLoop = nRecId != EXC_ID_CHEND;
433 // skip nested record groups
434 if( nRecId == EXC_ID_CHBEGIN )
435 SkipBlock( rStrm );
436 }
437}
438
439// Frame formatting ===========================================================
440
442{
443 maData.mnTLMode = rStrm.ReaduInt16();
444 maData.mnBRMode = rStrm.ReaduInt16();
445 /* According to the spec, the upper 16 bits of all members in the
446 rectangle are unused and may contain garbage. */
447 maData.maRect.mnX = rStrm.ReadInt16(); rStrm.Ignore( 2 );
448 maData.maRect.mnY = rStrm.ReadInt16(); rStrm.Ignore( 2 );
449 maData.maRect.mnWidth = rStrm.ReadInt16(); rStrm.Ignore( 2 );
450 maData.maRect.mnHeight = rStrm.ReadInt16(); rStrm.Ignore( 2 );
451}
452
454{
456 maData.mnPattern = rStrm.ReaduInt16();
458 maData.mnFlags = rStrm.ReaduInt16();
459
460 const XclImpRoot& rRoot = rStrm.GetRoot();
461 if( rRoot.GetBiff() == EXC_BIFF8 )
462 // BIFF8: index into palette used instead of RGB data
463 maData.maColor = rRoot.GetPalette().GetColor( rStrm.ReaduInt16() );
464}
465
467 ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
468{
469 const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
470 if( IsAuto() )
471 {
472 XclChLineFormat aLineFmt;
473 aLineFmt.maColor = (eObjType == EXC_CHOBJTYPE_LINEARSERIES) ?
474 rRoot.GetSeriesLineAutoColor( nFormatIdx ) :
475 rRoot.GetPalette().GetColor( rFmtInfo.mnAutoLineColorIdx );
477 aLineFmt.mnWeight = rFmtInfo.mnAutoLineWeight;
478 rRoot.ConvertLineFormat( rPropSet, aLineFmt, rFmtInfo.mePropMode );
479 }
480 else
481 {
482 rRoot.ConvertLineFormat( rPropSet, maData, rFmtInfo.mePropMode );
483 }
484}
485
487{
489 maData.mnPattern = rStrm.ReaduInt16();
490 maData.mnFlags = rStrm.ReaduInt16();
491
492 const XclImpRoot& rRoot = rStrm.GetRoot();
493 if( rRoot.GetBiff() == EXC_BIFF8 )
494 {
495 // BIFF8: index into palette used instead of RGB data
496 const XclImpPalette& rPal = rRoot.GetPalette();
497 maData.maPattColor = rPal.GetColor( rStrm.ReaduInt16() );
498 maData.maBackColor = rPal.GetColor( rStrm.ReaduInt16());
499 }
500}
501
503 ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
504{
505 const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
506 if( IsAuto() )
507 {
508 XclChAreaFormat aAreaFmt;
509 aAreaFmt.maPattColor = (eObjType == EXC_CHOBJTYPE_FILLEDSERIES) ?
510 rRoot.GetSeriesFillAutoColor( nFormatIdx ) :
511 rRoot.GetPalette().GetColor( rFmtInfo.mnAutoPattColorIdx );
512 aAreaFmt.mnPattern = EXC_PATT_SOLID;
513 rRoot.ConvertAreaFormat( rPropSet, aAreaFmt, rFmtInfo.mePropMode );
514 }
515 else
516 {
517 rRoot.ConvertAreaFormat( rPropSet, maData, rFmtInfo.mePropMode );
518 }
519}
520
522 mnDffFillType( mso_fillSolid )
523{
525 std::make_shared<SfxItemSet>( rRoot.GetDoc().GetDrawLayer()->GetItemPool() );
526}
527
529{
530 // read from stream - CHESCHERFORMAT uses own ID for record continuation
531 XclImpDffPropSet aPropSet( rStrm.GetRoot() );
532 rStrm.ResetRecord( true, rStrm.GetRecId() );
533 rStrm >> aPropSet;
534 // get the data
535 aPropSet.FillToItemSet( *maData.mxItemSet );
536 // get fill type from DFF property set
538}
539
541{
542 switch( rStrm.GetRecId() )
543 {
545 maPicFmt.mnBmpMode = rStrm.ReaduInt16();
546 rStrm.Ignore( 2 );
547 maPicFmt.mnFlags = rStrm.ReaduInt16();
549 break;
550 }
551}
552
554 ScfPropertySet& rPropSet, XclChObjectType eObjType, bool bUsePicFmt ) const
555{
556 const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
557 rRoot.ConvertEscherFormat( rPropSet, maData, bUsePicFmt ? &maPicFmt : nullptr, mnDffFillType, rFmtInfo.mePropMode );
558}
559
561{
562 if( !rFmtInfo.mbCreateDefFrame )
563 return;
564
565 switch( rFmtInfo.meDefFrameType )
566 {
569 if( rFmtInfo.mbIsFrame )
570 mxAreaFmt = std::make_shared<XclImpChAreaFormat>();
571 break;
573 {
574 XclChLineFormat aLineFmt;
575 ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, false );
577 mxLineFmt = new XclImpChLineFormat( aLineFmt );
578 if( rFmtInfo.mbIsFrame )
579 {
580 XclChAreaFormat aAreaFmt;
581 ::set_flag( aAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, false );
582 aAreaFmt.mnPattern = EXC_PATT_NONE;
583 mxAreaFmt = std::make_shared<XclImpChAreaFormat>( aAreaFmt );
584 }
585 }
586 break;
587 default:
588 OSL_FAIL( "XclImpChFrameBase::XclImpChFrameBase - unknown frame type" );
589 }
590}
591
593{
594 switch( rStrm.GetRecId() )
595 {
598 mxLineFmt->ReadChLineFormat( rStrm );
599 break;
601 mxAreaFmt = std::make_shared<XclImpChAreaFormat>();
602 mxAreaFmt->ReadChAreaFormat( rStrm );
603 break;
605 mxEscherFmt = std::make_shared<XclImpChEscherFormat>( rStrm.GetRoot() );
606 mxEscherFmt->ReadRecordGroup( rStrm );
607 break;
608 }
609}
610
612 ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
613{
614 if( mxLineFmt )
615 mxLineFmt->Convert( rRoot, rPropSet, eObjType, nFormatIdx );
616}
617
619 ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx, bool bUsePicFmt ) const
620{
621 if( rRoot.GetFormatInfo( eObjType ).mbIsFrame )
622 {
623 // CHESCHERFORMAT overrides CHAREAFORMAT (even if it is auto)
624 if( mxEscherFmt )
625 mxEscherFmt->Convert( rRoot, rPropSet, eObjType, bUsePicFmt );
626 else if( mxAreaFmt )
627 mxAreaFmt->Convert( rRoot, rPropSet, eObjType, nFormatIdx );
628 }
629}
630
632 ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx, bool bUsePicFmt ) const
633{
634 ConvertLineBase( rRoot, rPropSet, eObjType, nFormatIdx );
635 ConvertAreaBase( rRoot, rPropSet, eObjType, nFormatIdx, bUsePicFmt );
636}
637
639 XclImpChFrameBase( rRoot.GetFormatInfo( eObjType ) ),
640 XclImpChRoot( rRoot ),
641 meObjType( eObjType )
642{
643}
644
646{
647 maData.mnFormat = rStrm.ReaduInt16();
648 maData.mnFlags = rStrm.ReaduInt16();
649}
650
651void XclImpChFrame::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
652{
653 const XclImpPalette& rPal = GetPalette();
654
655 if( rLineData.IsVisible() && (!mxLineFmt || !mxLineFmt->HasLine()) )
656 {
657 // line formatting
658 XclChLineFormat aLineFmt;
659 aLineFmt.maColor = rPal.GetColor( rLineData.mnColorIdx );
660 switch( rLineData.mnStyle )
661 {
663 case EXC_OBJ_LINE_DASH: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASH; break;
664 case EXC_OBJ_LINE_DOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DOT; break;
670 case EXC_OBJ_LINE_NONE: aLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE; break;
671 default: aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
672 }
673 switch( rLineData.mnWidth )
674 {
675 case EXC_OBJ_LINE_HAIR: aLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR; break;
676 case EXC_OBJ_LINE_THIN: aLineFmt.mnWeight = EXC_CHLINEFORMAT_SINGLE; break;
679 default: aLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR;
680 }
681 ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, rLineData.IsAuto() );
682 mxLineFmt = new XclImpChLineFormat( aLineFmt );
683 }
684
685 if( rFillData.IsFilled() && (!mxAreaFmt || !mxAreaFmt->HasArea()) && !mxEscherFmt )
686 {
687 // area formatting
688 XclChAreaFormat aAreaFmt;
689 aAreaFmt.maPattColor = rPal.GetColor( rFillData.mnPattColorIdx );
690 aAreaFmt.maBackColor = rPal.GetColor( rFillData.mnBackColorIdx );
691 aAreaFmt.mnPattern = rFillData.mnPattern;
692 ::set_flag( aAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, rFillData.IsAuto() );
693 mxAreaFmt = std::make_shared<XclImpChAreaFormat>( aAreaFmt );
694 }
695}
696
697void XclImpChFrame::Convert( ScfPropertySet& rPropSet, bool bUsePicFmt ) const
698{
700}
701
702// Source links ===============================================================
703
704namespace {
705
707Reference< XLabeledDataSequence > lclCreateLabeledDataSequence(
708 const XclImpChSourceLinkRef& xValueLink, const OUString& rValueRole,
709 const XclImpChSourceLink* pTitleLink = nullptr )
710{
711 // create data sequence for values and title
712 Reference< XDataSequence > xValueSeq;
713 if( xValueLink )
714 xValueSeq = xValueLink->CreateDataSequence( rValueRole );
715 Reference< XDataSequence > xTitleSeq;
716 if( pTitleLink )
717 xTitleSeq = pTitleLink->CreateDataSequence( EXC_CHPROP_ROLE_LABEL );
718
719 // create the labeled data sequence, if values or title are present
720 Reference< XLabeledDataSequence > xLabeledSeq;
721 if( xValueSeq.is() || xTitleSeq.is() )
722 xLabeledSeq = LabeledDataSequence::create(comphelper::getProcessComponentContext());
723 if( xLabeledSeq.is() )
724 {
725 if( xValueSeq.is() )
726 xLabeledSeq->setValues( xValueSeq );
727 if( xTitleSeq.is() )
728 xLabeledSeq->setLabel( xTitleSeq );
729 }
730 return xLabeledSeq;
731}
732
733} // namespace
734
736 XclImpChRoot( rRoot )
737{
738}
739
741{
742}
743
745{
746 maData.mnDestType = rStrm.ReaduInt8();
747 maData.mnLinkType = rStrm.ReaduInt8();
748 maData.mnFlags = rStrm.ReaduInt16();
749 maData.mnNumFmtIdx = rStrm.ReaduInt16();
750
751 mxTokenArray.reset();
753 {
754 // read token array
755 XclTokenArray aXclTokArr;
756 rStrm >> aXclTokArr;
757
758 // convert BIFF formula tokens to Calc token array
759 if( std::unique_ptr<ScTokenArray> pTokens = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CHART, aXclTokArr ) )
760 mxTokenArray = std::move( pTokens );
761 }
762
763 // try to read a following CHSTRING record
764 if( (rStrm.GetNextRecId() == EXC_ID_CHSTRING) && rStrm.StartNextRecord() )
765 {
766 mxString = std::make_shared<XclImpString>();
767 rStrm.Ignore( 2 );
769 }
770}
771
772void XclImpChSourceLink::SetString( const OUString& rString )
773{
774 if( !mxString )
775 mxString = std::make_shared<XclImpString>();
776 mxString->SetText( rString );
777}
778
780{
781 if( mxString )
782 mxString->SetFormats( std::move(rFormats) );
783}
784
786{
787 sal_uInt32 nCellCount = 0;
788 if( mxTokenArray )
789 {
790 FormulaTokenArrayPlainIterator aIter(*mxTokenArray);
791 for( const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next() )
792 {
793 switch( pToken->GetType() )
794 {
795 case ::formula::svSingleRef:
796 case ::formula::svExternalSingleRef:
797 // single cell
798 ++nCellCount;
799 break;
800 case ::formula::svDoubleRef:
801 case ::formula::svExternalDoubleRef:
802 {
803 // cell range
804 const ScComplexRefData& rComplexRef = *pToken->GetDoubleRef();
805 ScAddress aAbs1 = rComplexRef.Ref1.toAbs(GetRoot().GetDoc(), ScAddress());
806 ScAddress aAbs2 = rComplexRef.Ref2.toAbs(GetRoot().GetDoc(), ScAddress());
807 sal_uInt32 nTabs = static_cast<sal_uInt32>(aAbs2.Tab() - aAbs1.Tab() + 1);
808 sal_uInt32 nCols = static_cast<sal_uInt32>(aAbs2.Col() - aAbs1.Col() + 1);
809 sal_uInt32 nRows = static_cast<sal_uInt32>(aAbs2.Row() - aAbs1.Row() + 1);
810 nCellCount += nCols * nRows * nTabs;
811 }
812 break;
813 default: ;
814 }
815 }
816 }
817 return limit_cast< sal_uInt16 >( nCellCount );
818}
819
820void XclImpChSourceLink::ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const
821{
822 bool bLinkToSource = ::get_flag( maData.mnFlags, EXC_CHSRCLINK_NUMFMT );
823 sal_uInt32 nScNumFmt = bLinkToSource ? GetNumFmtBuffer().GetScFormat( maData.mnNumFmtIdx ) : NUMBERFORMAT_ENTRY_NOT_FOUND;
824 OUString aPropName = bPercent ? OUString( EXC_CHPROP_PERCENTAGENUMFMT ) : OUString( EXC_CHPROP_NUMBERFORMAT );
825 if( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND )
826 rPropSet.SetProperty( aPropName, static_cast< sal_Int32 >( nScNumFmt ) );
827 else
828 // restore 'link to source' at data point (series may contain manual number format)
829 rPropSet.SetAnyProperty( aPropName, Any() );
830}
831
832Reference< XDataSequence > XclImpChSourceLink::CreateDataSequence( const OUString& rRole ) const
833{
834 Reference< XDataSequence > xDataSeq;
835 Reference< XDataProvider > xDataProv = GetDataProvider();
836 if( xDataProv.is() )
837 {
838 if ( mxTokenArray )
839 {
840 ScCompiler aComp( GetDoc(), ScAddress(), *mxTokenArray, GetDoc().GetGrammar() );
841 OUStringBuffer aRangeRep;
842 aComp.CreateStringFromTokenArray( aRangeRep );
843 try
844 {
845 xDataSeq = xDataProv->createDataSequenceByRangeRepresentation( aRangeRep.makeStringAndClear() );
846 // set sequence role
847 ScfPropertySet aSeqProp( xDataSeq );
848 aSeqProp.SetProperty( EXC_CHPROP_ROLE, rRole );
849 }
850 catch( Exception& )
851 {
852 // OSL_FAIL( "XclImpChSourceLink::CreateDataSequence - cannot create data sequence" );
853 }
854 }
855 else if( rRole == EXC_CHPROP_ROLE_LABEL && mxString && !mxString->GetText().isEmpty() )
856 {
857 try
858 {
859 OUString aString("\"");
860 xDataSeq = xDataProv->createDataSequenceByRangeRepresentation( aString + mxString->GetText() + aString );
861 // set sequence role
862 ScfPropertySet aSeqProp( xDataSeq );
863 aSeqProp.SetProperty( EXC_CHPROP_ROLE, rRole );
864 }
865 catch( Exception& ) { }
866 }
867 }
868 return xDataSeq;
869}
870
871Sequence< Reference< XFormattedString > > XclImpChSourceLink::CreateStringSequence(
872 const XclImpChRoot& rRoot, sal_uInt16 nLeadFontIdx, const Color& rLeadFontColor ) const
873{
874 ::std::vector< Reference< XFormattedString > > aStringVec;
875 if( mxString )
876 {
877 for( XclImpStringIterator aIt( *mxString ); aIt.Is(); ++aIt )
878 {
879 Reference< css::chart2::XFormattedString2 > xFmtStr = css::chart2::FormattedString::create( comphelper::getProcessComponentContext() );
880 // set text data
881 xFmtStr->setString( aIt.GetPortionText() );
882
883 // set font formatting and font color
884 ScfPropertySet aStringProp( xFmtStr );
885 sal_uInt16 nFontIdx = aIt.GetPortionFont();
886 if( (nFontIdx == EXC_FONT_NOTFOUND) && (aIt.GetPortionIndex() == 0) )
887 // leading unformatted portion - use passed font settings
888 rRoot.ConvertFont( aStringProp, nLeadFontIdx, &rLeadFontColor );
889 else
890 rRoot.ConvertFont( aStringProp, nFontIdx );
891
892 // add string to vector of strings
893 aStringVec.emplace_back(xFmtStr );
894 }
895 }
896 return ScfApiHelper::VectorToSequence( aStringVec );
897}
898
899void XclImpChSourceLink::FillSourceLink( ::std::vector< ScTokenRef >& rTokens ) const
900{
901 if( !mxTokenArray )
902 // no links to fill.
903 return;
904
905 FormulaTokenArrayPlainIterator aIter(*mxTokenArray);
906 for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
907 {
908 ScTokenRef pToken(p->Clone());
909 if (ScRefTokenHelper::isRef(pToken))
910 // This is a reference token. Store it.
911 ScRefTokenHelper::join(&GetRoot().GetDoc(), rTokens, pToken, ScAddress());
912 }
913}
914
915// Text =======================================================================
916
918{
919}
920
922{
923 Color aFontColor = GetFontColor();
924 rRoot.ConvertFont( rPropSet, GetFontIndex(), &aFontColor );
925}
926
927void XclImpChFontBase::ConvertRotationBase( ScfPropertySet& rPropSet, bool bSupportsStacked ) const
928{
929 XclChPropSetHelper::WriteRotationProperties( rPropSet, GetRotation(), bSupportsStacked );
930}
931
933 mnFontIdx( EXC_FONT_NOTFOUND )
934{
935}
936
938{
939 mnFontIdx = rStrm.ReaduInt16();
940}
941
943 XclImpChRoot( rRoot )
944{
945}
946
948{
949 maData.mnHAlign = rStrm.ReaduInt8();
950 maData.mnVAlign = rStrm.ReaduInt8();
951 maData.mnBackMode = rStrm.ReaduInt16();
953 >> maData.maRect;
954 maData.mnFlags = rStrm.ReaduInt16();
955
956 if( GetBiff() == EXC_BIFF8 )
957 {
958 // BIFF8: index into palette used instead of RGB data
959 maData.maTextColor = GetPalette().GetColor( rStrm.ReaduInt16() );
960 // placement and rotation
961 maData.mnFlags2 = rStrm.ReaduInt16();
962 maData.mnRotation = rStrm.ReaduInt16();
963 }
964 else
965 {
966 // BIFF2-BIFF7: get rotation from text orientation
967 sal_uInt8 nOrient = ::extract_value< sal_uInt8 >( maData.mnFlags, 8, 3 );
969 }
970}
971
973{
974 switch( rStrm.GetRecId() )
975 {
977 mxFramePos = std::make_shared<XclImpChFramePos>();
978 mxFramePos->ReadChFramePos( rStrm );
979 break;
980 case EXC_ID_CHFONT:
981 mxFont = std::make_shared<XclImpChFont>();
982 mxFont->ReadChFont( rStrm );
983 break;
985 if( GetBiff() == EXC_BIFF8 )
987 break;
989 mxSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
990 mxSrcLink->ReadChSourceLink( rStrm );
991 break;
992 case EXC_ID_CHFRAME:
993 mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_TEXT );
994 mxFrame->ReadRecordGroup( rStrm );
995 break;
997 maObjLink.mnTarget = rStrm.ReaduInt16();
998 maObjLink.maPointPos.mnSeriesIdx = rStrm.ReaduInt16();
999 maObjLink.maPointPos.mnPointIdx = rStrm.ReaduInt16();
1000 break;
1003 break;
1004 case EXC_ID_CHEND:
1005 if( mxSrcLink && !maFormats.empty() )
1006 mxSrcLink->SetTextFormats( std::vector(maFormats) );
1007 break;
1008 }
1009}
1010
1012{
1013 return mxFont ? mxFont->GetFontIndex() : EXC_FONT_NOTFOUND;
1014}
1015
1017{
1019}
1020
1022{
1023 return maData.mnRotation;
1024}
1025
1026void XclImpChText::SetString( const OUString& rString )
1027{
1028 if( !mxSrcLink )
1029 mxSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
1030 mxSrcLink->SetString( rString );
1031}
1032
1033void XclImpChText::UpdateText( const XclImpChText* pParentText )
1034{
1035 if( !pParentText )
1036 return;
1037
1038 // update missing members
1039 if( !mxFrame )
1040 mxFrame = pParentText->mxFrame;
1041 if( !mxFont )
1042 {
1043 mxFont = pParentText->mxFont;
1044 // text color is taken from CHTEXT record, not from font in CHFONT
1046 maData.maTextColor = pParentText->maData.maTextColor;
1047 }
1048}
1049
1050void XclImpChText::UpdateDataLabel( bool bCateg, bool bValue, bool bPercent )
1051{
1055 ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC, bCateg && bPercent );
1056 ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED, !bCateg && !bValue && !bPercent );
1057}
1058
1060{
1061 ConvertFontBase( GetChRoot(), rPropSet );
1062}
1063
1064void XclImpChText::ConvertRotation( ScfPropertySet& rPropSet, bool bSupportsStacked ) const
1065{
1066 ConvertRotationBase( rPropSet, bSupportsStacked );
1067}
1068
1070{
1071 if( mxFrame )
1072 mxFrame->Convert( rPropSet );
1073}
1074
1075void XclImpChText::ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const
1076{
1077 if( mxSrcLink )
1078 mxSrcLink->ConvertNumFmt( rPropSet, bPercent );
1079}
1080
1081void XclImpChText::ConvertDataLabel( ScfPropertySet& rPropSet, const XclChTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet ) const
1082{
1083 // existing CHFRLABELPROPS record wins over flags from CHTEXT
1084 sal_uInt16 nShowFlags = mxLabelProps ? mxLabelProps->mnFlags : maData.mnFlags;
1089
1090 // get raw flags for label values
1091 bool bShowNone = IsDeleted();
1092 bool bShowCateg = !bShowNone && ::get_flag( nShowFlags, SHOWANYCATEG );
1093 bool bShowPercent = !bShowNone && ::get_flag( nShowFlags, SHOWANYPERCENT );
1094 bool bShowValue = !bShowNone && ::get_flag( nShowFlags, SHOWANYVALUE );
1095 bool bShowBubble = !bShowNone && ::get_flag( nShowFlags, SHOWANYBUBBLE );
1096
1097 // adjust to Chart2 behaviour
1098 if( rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES )
1099 bShowValue = bShowBubble; // Chart2 bubble charts show bubble size if 'ShowValue' is set
1100
1101 // other flags
1102 bool bShowAny = bShowValue || bShowPercent || bShowCateg;
1103 bool bShowSymbol = bShowAny && ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWSYMBOL );
1104
1105 // create API struct for label values, set API label separator
1106 cssc2::DataPointLabel aPointLabel( bShowValue, bShowPercent, bShowCateg, bShowSymbol, false, false );
1107 rPropSet.SetProperty( EXC_CHPROP_LABEL, aPointLabel );
1108 OUString aSep = mxLabelProps ? mxLabelProps->maSeparator : OUString('\n');
1109 if( aSep.isEmpty() )
1110 aSep = "; ";
1112
1113 // text properties of attached label
1114 if( !bShowAny )
1115 return;
1116
1117 ConvertFont( rPropSet );
1118 ConvertRotation( rPropSet, false );
1119 // label placement
1120 using namespace cssc::DataLabelPlacement;
1121 sal_Int32 nPlacement = rTypeInfo.mnDefaultLabelPos;
1122 switch( ::extract_value< sal_uInt16 >( maData.mnFlags2, 0, 4 ) )
1123 {
1124 case EXC_CHTEXT_POS_DEFAULT: nPlacement = rTypeInfo.mnDefaultLabelPos; break;
1125 case EXC_CHTEXT_POS_OUTSIDE: nPlacement = OUTSIDE; break;
1126 case EXC_CHTEXT_POS_INSIDE: nPlacement = INSIDE; break;
1127 case EXC_CHTEXT_POS_CENTER: nPlacement = CENTER; break;
1128 case EXC_CHTEXT_POS_AXIS: nPlacement = NEAR_ORIGIN; break;
1129 case EXC_CHTEXT_POS_ABOVE: nPlacement = TOP; break;
1130 case EXC_CHTEXT_POS_BELOW: nPlacement = BOTTOM; break;
1131 case EXC_CHTEXT_POS_LEFT: nPlacement = LEFT; break;
1132 case EXC_CHTEXT_POS_RIGHT: nPlacement = RIGHT; break;
1133 case EXC_CHTEXT_POS_AUTO: nPlacement = AVOID_OVERLAP; break;
1134 }
1135 sal_Int32 nGlobalPlacement = 0;
1136 if ( ( nPlacement == rTypeInfo.mnDefaultLabelPos ) && pGlobalPropSet &&
1137 pGlobalPropSet->GetProperty( nGlobalPlacement, EXC_CHPROP_LABELPLACEMENT ) )
1138 nPlacement = nGlobalPlacement;
1139
1140 rPropSet.SetProperty( EXC_CHPROP_LABELPLACEMENT, nPlacement );
1141 // label number format (percentage format wins over value format)
1142 if( bShowPercent || bShowValue )
1143 ConvertNumFmt( rPropSet, bShowPercent );
1144}
1145
1146Reference< XTitle > XclImpChText::CreateTitle() const
1147{
1148 Reference< XTitle > xTitle;
1149 if( mxSrcLink && mxSrcLink->HasString() )
1150 {
1151 // create the formatted strings
1152 Sequence< Reference< XFormattedString > > aStringSeq(
1153 mxSrcLink->CreateStringSequence( GetChRoot(), GetFontIndex(), GetFontColor() ) );
1154 if( aStringSeq.hasElements() )
1155 {
1156 // create the title object
1157 xTitle.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_TITLE ), UNO_QUERY );
1158 if( xTitle.is() )
1159 {
1160 // set the formatted strings
1161 xTitle->setText( aStringSeq );
1162 // more title formatting properties
1163 ScfPropertySet aTitleProp( xTitle );
1164 ConvertFrame( aTitleProp );
1165 ConvertRotation( aTitleProp, true );
1166 }
1167 }
1168 }
1169 return xTitle;
1170}
1171
1173{
1174 if( !mxFramePos ) return;
1175
1176 const XclChFramePos& rPosData = mxFramePos->GetFramePosData();
1177 OSL_ENSURE( (rPosData.mnTLMode == EXC_CHFRAMEPOS_PARENT) && (rPosData.mnBRMode == EXC_CHFRAMEPOS_PARENT),
1178 "XclImpChText::ConvertTitlePosition - unexpected frame position mode" );
1179
1180 /* Check if title is moved manually. To get the actual position of the
1181 title, we do some kind of hack and use the values from the CHTEXT
1182 record, effectively ignoring the contents of the CHFRAMEPOS record
1183 which contains the position relative to the default title position
1184 (according to the spec, the CHFRAMEPOS supersedes the CHTEXT record).
1185 Especially when it comes to axis titles, things would become very
1186 complicated here, because the relative title position is stored in a
1187 measurement unit that is dependent on the size of the inner plot area,
1188 the interpretation of the X and Y coordinate is dependent on the
1189 direction of the axis, and in 3D charts, and the title default
1190 positions are dependent on the 3D view settings (rotation, elevation,
1191 and perspective). Thus, it is easier to assume that the creator has
1192 written out the correct absolute position and size of the title in the
1193 CHTEXT record. This is assured by checking that the shape size stored
1194 in the CHTEXT record is non-zero. */
1195 if( !((rPosData.mnTLMode == EXC_CHFRAMEPOS_PARENT) &&
1196 ((rPosData.maRect.mnX != 0) || (rPosData.maRect.mnY != 0)) &&
1197 (maData.maRect.mnWidth > 0) && (maData.maRect.mnHeight > 0)) )
1198 return;
1199
1200 try
1201 {
1202 Reference< XShape > xTitleShape( GetTitleShape( rTitleKey ), UNO_SET_THROW );
1203 // the call to XShape.getSize() may recalc the chart view
1204 css::awt::Size aTitleSize = xTitleShape->getSize();
1205 // rotated titles need special handling...
1206 Degree100 nScRot = XclTools::GetScRotation( GetRotation(), 0_deg100 );
1207 double fRad = toRadians(nScRot);
1208 double fSin = fabs( sin( fRad ) );
1209 // calculate the title position from the values in the CHTEXT record
1210 css::awt::Point aTitlePos(
1213 // add part of height to X direction, if title is rotated down (clockwise)
1214 if( nScRot > 18000_deg100 )
1215 aTitlePos.X += static_cast< sal_Int32 >( fSin * aTitleSize.Height + 0.5 );
1216 // add part of width to Y direction, if title is rotated up (counterclockwise)
1217 else if( nScRot > 0_deg100 )
1218 aTitlePos.Y += static_cast< sal_Int32 >( fSin * aTitleSize.Width + 0.5 );
1219 // set the resulting position at the title shape
1220 xTitleShape->setPosition( aTitlePos );
1221 }
1222 catch( Exception& )
1223 {
1224 }
1225}
1226
1228{
1229 if( GetBiff() == EXC_BIFF8 )
1230 {
1231 mxLabelProps = std::make_shared<XclChFrLabelProps>();
1232 sal_uInt16 nSepLen;
1233 rStrm.Ignore( 12 );
1234 mxLabelProps->mnFlags = rStrm.ReaduInt16();
1235 nSepLen = rStrm.ReaduInt16();
1236 if( nSepLen > 0 )
1237 mxLabelProps->maSeparator = rStrm.ReadUniString( nSepLen );
1238 }
1239}
1240
1241namespace {
1242
1243void lclUpdateText( XclImpChTextRef& rxText, const XclImpChText* xDefText )
1244{
1245 if (rxText)
1246 rxText->UpdateText( xDefText );
1247 else if (xDefText)
1248 {
1249 rxText = std::make_shared<XclImpChText>(*xDefText);
1250 }
1251}
1252
1253void lclFinalizeTitle( XclImpChTextRef& rxTitle, const XclImpChText* pDefText, const OUString& rAutoTitle )
1254{
1255 /* Do not update a title, if it is not visible (if rxTitle is null).
1256 Existing reference indicates enabled title. */
1257 if( rxTitle )
1258 {
1259 if( !rxTitle->HasString() )
1260 rxTitle->SetString( rAutoTitle );
1261 if( rxTitle->HasString() )
1262 rxTitle->UpdateText(pDefText);
1263 else
1264 rxTitle.reset();
1265 }
1266}
1267
1268} // namespace
1269
1270// Data series ================================================================
1271
1273{
1275 maData.mnMarkerType = rStrm.ReaduInt16();
1276 maData.mnFlags = rStrm.ReaduInt16();
1277
1278 const XclImpRoot& rRoot = rStrm.GetRoot();
1279 if( rRoot.GetBiff() == EXC_BIFF8 )
1280 {
1281 // BIFF8: index into palette used instead of RGB data
1282 const XclImpPalette& rPal = rRoot.GetPalette();
1283 maData.maLineColor = rPal.GetColor( rStrm.ReaduInt16() );
1284 maData.maFillColor = rPal.GetColor( rStrm.ReaduInt16() );
1285 // marker size
1286 maData.mnMarkerSize = rStrm.ReaduInt32();
1287 }
1288}
1289
1291 ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx, sal_Int16 nLineWeight ) const
1292{
1293 if( IsAuto() )
1294 {
1295 XclChMarkerFormat aMarkerFmt;
1296 // line and fill color of the symbol are equal to series line color
1297 //TODO: Excel sets no fill color for specific symbols (e.g. cross)
1298 aMarkerFmt.maLineColor = aMarkerFmt.maFillColor = rRoot.GetSeriesLineAutoColor( nFormatIdx );
1299 switch( nLineWeight )
1300 {
1305 default: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_SINGLESIZE;
1306 }
1307 aMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx );
1308 XclChPropSetHelper::WriteMarkerProperties( rPropSet, aMarkerFmt );
1309 }
1310 else
1311 {
1313 }
1314}
1315
1317 ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const
1318{
1319 Color aLineColor = IsAuto() ? rRoot.GetSeriesLineAutoColor( nFormatIdx ) : maData.maFillColor;
1320 rPropSet.SetColorProperty( EXC_CHPROP_COLOR, aLineColor );
1321}
1322
1324 mnPieDist( 0 )
1325{
1326}
1327
1329{
1330 mnPieDist = rStrm.ReaduInt16();
1331}
1332
1334{
1335 double fApiDist = ::std::min< double >( mnPieDist / 100.0, 1.0 );
1336 rPropSet.SetProperty( EXC_CHPROP_OFFSET, fApiDist );
1337}
1338
1340 mnFlags( 0 )
1341{
1342}
1343
1345{
1346 mnFlags = rStrm.ReaduInt16();
1347}
1348
1350{
1351 maData.mnBase = rStrm.ReaduInt8();
1352 maData.mnTop = rStrm.ReaduInt8();
1353}
1354
1356{
1357 using namespace ::com::sun::star::chart2::DataPointGeometry3D;
1358 sal_Int32 nApiType = (maData.mnBase == EXC_CH3DDATAFORMAT_RECT) ?
1359 ((maData.mnTop == EXC_CH3DDATAFORMAT_STRAIGHT) ? CUBOID : PYRAMID) :
1360 ((maData.mnTop == EXC_CH3DDATAFORMAT_STRAIGHT) ? CYLINDER : CONE);
1361 rPropSet.SetProperty( EXC_CHPROP_GEOMETRY3D, nApiType );
1362}
1363
1365 XclImpChRoot( rRoot ),
1366 mnFlags( 0 )
1367{
1368}
1369
1371{
1372 mnFlags = rStrm.ReaduInt16();
1373}
1374
1376{
1377 const sal_uInt16 EXC_CHATTLABEL_SHOWANYVALUE = EXC_CHATTLABEL_SHOWVALUE;
1378 const sal_uInt16 EXC_CHATTLABEL_SHOWANYPERCENT = EXC_CHATTLABEL_SHOWPERCENT | EXC_CHATTLABEL_SHOWCATEGPERC;
1379 const sal_uInt16 EXC_CHATTLABEL_SHOWANYCATEG = EXC_CHATTLABEL_SHOWCATEG | EXC_CHATTLABEL_SHOWCATEGPERC;
1380
1381 XclImpChTextRef xLabel;
1382 if ( pParent )
1383 xLabel = std::make_shared<XclImpChText>( *pParent );
1384 else
1385 xLabel = std::make_shared<XclImpChText>( GetChRoot() );
1386 xLabel->UpdateDataLabel(
1387 ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYCATEG ),
1388 ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYVALUE ),
1389 ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYPERCENT ) );
1390 return xLabel;
1391}
1392
1394 XclImpChRoot( rRoot )
1395{
1396}
1397
1399{
1400 maData.maPointPos.mnPointIdx = rStrm.ReaduInt16();
1401 maData.maPointPos.mnSeriesIdx = rStrm.ReaduInt16();
1402 maData.mnFormatIdx = rStrm.ReaduInt16();
1403 maData.mnFlags = rStrm.ReaduInt16();
1404}
1405
1407{
1408 switch( rStrm.GetRecId() )
1409 {
1411 mxMarkerFmt = std::make_shared<XclImpChMarkerFormat>();
1412 mxMarkerFmt->ReadChMarkerFormat( rStrm );
1413 break;
1414 case EXC_ID_CHPIEFORMAT:
1415 mxPieFmt = std::make_shared<XclImpChPieFormat>();
1416 mxPieFmt->ReadChPieFormat( rStrm );
1417 break;
1419 mxSeriesFmt = std::make_shared<XclImpChSeriesFormat>();
1420 mxSeriesFmt->ReadChSeriesFormat( rStrm );
1421 break;
1423 mx3dDataFmt = std::make_shared<XclImpCh3dDataFormat>();
1424 mx3dDataFmt->ReadCh3dDataFormat( rStrm );
1425 break;
1427 mxAttLabel = std::make_shared<XclImpChAttachedLabel>( GetChRoot() );
1428 mxAttLabel->ReadChAttachedLabel( rStrm );
1429 break;
1430 default:
1432 }
1433}
1434
1435void XclImpChDataFormat::SetPointPos( const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx )
1436{
1437 maData.maPointPos = rPointPos;
1438 maData.mnFormatIdx = nFormatIdx;
1439}
1440
1442{
1443 // remove formats not used for the current chart type
1444 RemoveUnusedFormats( rTypeInfo );
1445}
1446
1448{
1449 // update missing formats from passed chart type group format
1450 if( pGroupFmt )
1451 {
1452 if( !mxLineFmt )
1453 mxLineFmt = pGroupFmt->mxLineFmt;
1454 if( !mxAreaFmt && !mxEscherFmt )
1455 {
1456 mxAreaFmt = pGroupFmt->mxAreaFmt;
1457 mxEscherFmt = pGroupFmt->mxEscherFmt;
1458 }
1459 if( !mxMarkerFmt )
1460 mxMarkerFmt = pGroupFmt->mxMarkerFmt;
1461 if( !mxPieFmt )
1462 mxPieFmt = pGroupFmt->mxPieFmt;
1463 if( !mxSeriesFmt )
1464 mxSeriesFmt = pGroupFmt->mxSeriesFmt;
1465 if( !mx3dDataFmt )
1466 mx3dDataFmt = pGroupFmt->mx3dDataFmt;
1467 if( !mxAttLabel )
1468 mxAttLabel = pGroupFmt->mxAttLabel;
1469 }
1470
1471 /* Create missing but required formats. Existing line, area, and marker
1472 format objects are needed to create automatic series formatting. */
1473 if( !mxLineFmt )
1475 if( !mxAreaFmt && !mxEscherFmt )
1476 mxAreaFmt = std::make_shared<XclImpChAreaFormat>();
1477 if( !mxMarkerFmt )
1478 mxMarkerFmt = std::make_shared<XclImpChMarkerFormat>();
1479
1480 // remove formats not used for the current chart type
1481 RemoveUnusedFormats( rTypeInfo );
1482 // update data label
1483 UpdateDataLabel( pGroupFmt );
1484}
1485
1487{
1488 // remove formats if they are automatic in this and in the passed series format
1489 if( pSeriesFmt )
1490 {
1491 if( IsAutoLine() && pSeriesFmt->IsAutoLine() )
1492 mxLineFmt.clear();
1493 if( IsAutoArea() && pSeriesFmt->IsAutoArea() )
1494 mxAreaFmt.reset();
1495 if( IsAutoMarker() && pSeriesFmt->IsAutoMarker() )
1496 mxMarkerFmt.reset();
1497 mxSeriesFmt.reset();
1498 }
1499
1500 // Excel ignores 3D bar format for single data points
1501 mx3dDataFmt.reset();
1502 // remove point line formats for linear chart types, TODO: implement in OOChart
1503 if( !rTypeInfo.IsSeriesFrameFormat() )
1504 mxLineFmt.clear();
1505
1506 // remove formats not used for the current chart type
1507 RemoveUnusedFormats( rTypeInfo );
1508 // update data label
1509 UpdateDataLabel( pSeriesFmt );
1510}
1511
1513{
1514 if( !mxLineFmt )
1516 mxAreaFmt.reset();
1517 mxEscherFmt.reset();
1518 mxMarkerFmt.reset();
1519 mxPieFmt.reset();
1520 mxSeriesFmt.reset();
1521 mx3dDataFmt.reset();
1522 mxAttLabel.reset();
1523 // update data label
1524 UpdateDataLabel( nullptr );
1525}
1526
1527void XclImpChDataFormat::Convert( ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet ) const
1528{
1529 /* Line and area format.
1530 #i71810# If the data points are filled with bitmaps, textures, or
1531 patterns, then only bar charts will use the CHPICFORMAT record to
1532 determine stacking/stretching mode. All other chart types ignore this
1533 record and always use the property 'fill-type' from the DFF property
1534 set (stretched for bitmaps, and stacked for textures and patterns). */
1535 bool bUsePicFmt = rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR;
1536 ConvertFrameBase( GetChRoot(), rPropSet, rTypeInfo.GetSeriesObjectType(), maData.mnFormatIdx, bUsePicFmt );
1537
1538 // #i83151# only hair lines in 3D charts with filled data points
1539 if( rTypeInfo.mb3dChart && rTypeInfo.IsSeriesFrameFormat() && mxLineFmt && mxLineFmt->HasLine() )
1540 rPropSet.SetProperty< sal_Int32 >( "BorderWidth", 0 );
1541
1542 // other formatting
1543 if( mxMarkerFmt )
1544 mxMarkerFmt->Convert( GetChRoot(), rPropSet, maData.mnFormatIdx, GetLineWeight() );
1545 if( mxPieFmt )
1546 mxPieFmt->Convert( rPropSet );
1547 if( mx3dDataFmt )
1548 mx3dDataFmt->Convert( rPropSet );
1549 if( mxLabel )
1550 mxLabel->ConvertDataLabel( rPropSet, rTypeInfo, pGlobalPropSet );
1551
1552 // 3D settings
1553 rPropSet.SetProperty< sal_Int16 >( EXC_CHPROP_PERCENTDIAGONAL, 0 );
1554
1555 /* Special case: set marker color as line color, if series line is not
1556 visible. This makes the color visible in the marker area.
1557 TODO: remove this if OOChart supports own colors in markers. */
1558 if( !rTypeInfo.IsSeriesFrameFormat() && !HasLine() && mxMarkerFmt )
1559 mxMarkerFmt->ConvertColor( GetChRoot(), rPropSet, maData.mnFormatIdx );
1560}
1561
1563{
1564 ConvertLineBase( GetChRoot(), rPropSet, eObjType );
1565}
1566
1567void XclImpChDataFormat::ConvertArea( ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const
1568{
1569 ConvertAreaBase( GetChRoot(), rPropSet, EXC_CHOBJTYPE_FILLEDSERIES, nFormatIdx );
1570}
1571
1573{
1574 // data point marker only in linear 2D charts
1575 if( rTypeInfo.IsSeriesFrameFormat() )
1576 mxMarkerFmt.reset();
1577 // pie format only in pie/donut charts
1578 if( rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE )
1579 mxPieFmt.reset();
1580 // 3D format only in 3D bar charts
1581 if( !rTypeInfo.mb3dChart || (rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_BAR) )
1582 mx3dDataFmt.reset();
1583}
1584
1586{
1587 /* CHTEXT groups linked to data labels override existing CHATTACHEDLABEL
1588 records. Only if there is a CHATTACHEDLABEL record without a CHTEXT
1589 group, the contents of the CHATTACHEDLABEL record are used. In this
1590 case a new CHTEXT group is created and filled with the settings from
1591 the CHATTACHEDLABEL record. */
1592 const XclImpChText* pDefText = nullptr;
1593 if (pParentFmt)
1594 pDefText = pParentFmt->GetDataLabel();
1595 if (!pDefText)
1597 if (mxLabel)
1598 mxLabel->UpdateText(pDefText);
1599 else if (mxAttLabel)
1600 mxLabel = mxAttLabel->CreateDataLabel( pDefText );
1601}
1602
1604 XclImpChRoot( rRoot )
1605{
1606}
1607
1609{
1610 maData.mnLineType = rStrm.ReaduInt8();
1611 maData.mnOrder = rStrm.ReaduInt8();
1613 maData.mnShowEquation = rStrm.ReaduInt8();
1614 maData.mnShowRSquared = rStrm.ReaduInt8();
1617}
1618
1619Reference< XRegressionCurve > XclImpChSerTrendLine::CreateRegressionCurve() const
1620{
1621 // trend line type
1622 Reference< XRegressionCurve > xRegCurve;
1623 switch( maData.mnLineType )
1624 {
1626 if( maData.mnOrder == 1 )
1627 {
1628 xRegCurve = LinearRegressionCurve::create( comphelper::getProcessComponentContext() );
1629 } else {
1630 xRegCurve = PolynomialRegressionCurve::create( comphelper::getProcessComponentContext() );
1631 }
1632 break;
1634 xRegCurve = ExponentialRegressionCurve::create( comphelper::getProcessComponentContext() );
1635 break;
1637 xRegCurve = LogarithmicRegressionCurve::create( comphelper::getProcessComponentContext() );
1638 break;
1640 xRegCurve = PotentialRegressionCurve::create( comphelper::getProcessComponentContext() );
1641 break;
1643 xRegCurve = MovingAverageRegressionCurve::create( comphelper::getProcessComponentContext() );
1644 break;
1645 }
1646
1647 // trend line formatting
1648 if( xRegCurve.is() && mxDataFmt )
1649 {
1650 ScfPropertySet aPropSet( xRegCurve );
1651 mxDataFmt->ConvertLine( aPropSet, EXC_CHOBJTYPE_TRENDLINE );
1652
1654 aPropSet.SetProperty(EXC_CHPROP_POLYNOMIAL_DEGREE, static_cast<sal_Int32> (maData.mnOrder) );
1655 aPropSet.SetProperty(EXC_CHPROP_MOVING_AVERAGE_PERIOD, static_cast<sal_Int32> (maData.mnOrder) );
1658
1659 bool bForceIntercept = std::isfinite(maData.mfIntercept);
1660 aPropSet.SetProperty(EXC_CHPROP_FORCE_INTERCEPT, bForceIntercept);
1661 if (bForceIntercept)
1662 {
1664 }
1665
1666 // #i83100# show equation and correlation coefficient
1667 ScfPropertySet aLabelProp( xRegCurve->getEquationProperties() );
1670
1671 // #i83100# formatting of the equation text box
1672 if (const XclImpChText* pLabel = mxDataFmt->GetDataLabel())
1673 {
1674 pLabel->ConvertFont( aLabelProp );
1675 pLabel->ConvertFrame( aLabelProp );
1676 pLabel->ConvertNumFmt( aLabelProp, false );
1677 }
1678 }
1679
1680 return xRegCurve;
1681}
1682
1684 XclImpChRoot( rRoot )
1685{
1686}
1687
1689{
1690 maData.mnBarType = rStrm.ReaduInt8();
1691 maData.mnSourceType = rStrm.ReaduInt8();
1692 maData.mnLineEnd = rStrm.ReaduInt8();
1693 rStrm.Ignore( 1 );
1695 maData.mnValueCount = rStrm.ReaduInt16();
1696}
1697
1699{
1700 mxValueLink = xValueLink;
1701 mxDataFmt = xDataFmt;
1702}
1703
1704Reference< XLabeledDataSequence > XclImpChSerErrorBar::CreateValueSequence() const
1705{
1706 return lclCreateLabeledDataSequence( mxValueLink, XclChartHelper::GetErrorBarValuesRole( maData.mnBarType ) );
1707}
1708
1709Reference< XPropertySet > XclImpChSerErrorBar::CreateErrorBar( const XclImpChSerErrorBar* pPosBar, const XclImpChSerErrorBar* pNegBar )
1710{
1711 Reference< XPropertySet > xErrorBar;
1712
1713 if( const XclImpChSerErrorBar* pPrimaryBar = pPosBar ? pPosBar : pNegBar )
1714 {
1715 xErrorBar.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_ERRORBAR ), UNO_QUERY );
1716 ScfPropertySet aBarProp( xErrorBar );
1717
1718 // plus/minus bars visible?
1719 aBarProp.SetBoolProperty( EXC_CHPROP_SHOWPOSITIVEERROR, pPosBar != nullptr );
1720 aBarProp.SetBoolProperty( EXC_CHPROP_SHOWNEGATIVEERROR, pNegBar != nullptr );
1721
1722 // type of displayed error
1723 switch( pPrimaryBar->maData.mnSourceType )
1724 {
1726 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::RELATIVE );
1727 aBarProp.SetProperty( EXC_CHPROP_POSITIVEERROR, pPrimaryBar->maData.mfValue );
1728 aBarProp.SetProperty( EXC_CHPROP_NEGATIVEERROR, pPrimaryBar->maData.mfValue );
1729 break;
1730 case EXC_CHSERERR_FIXED:
1731 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::ABSOLUTE );
1732 aBarProp.SetProperty( EXC_CHPROP_POSITIVEERROR, pPrimaryBar->maData.mfValue );
1733 aBarProp.SetProperty( EXC_CHPROP_NEGATIVEERROR, pPrimaryBar->maData.mfValue );
1734 break;
1736 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::STANDARD_DEVIATION );
1737 aBarProp.SetProperty( EXC_CHPROP_WEIGHT, pPrimaryBar->maData.mfValue );
1738 break;
1740 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::STANDARD_ERROR );
1741 break;
1743 {
1744 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::FROM_DATA );
1745 // attach data sequences to error bar
1746 Reference< XDataSink > xDataSink( xErrorBar, UNO_QUERY );
1747 if( xDataSink.is() )
1748 {
1749 // create vector of all value sequences
1750 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
1751 // add positive values
1752 if( pPosBar )
1753 {
1754 Reference< XLabeledDataSequence > xValueSeq = pPosBar->CreateValueSequence();
1755 if( xValueSeq.is() )
1756 aLabeledSeqVec.push_back( xValueSeq );
1757 }
1758 // add negative values
1759 if( pNegBar )
1760 {
1761 Reference< XLabeledDataSequence > xValueSeq = pNegBar->CreateValueSequence();
1762 if( xValueSeq.is() )
1763 aLabeledSeqVec.push_back( xValueSeq );
1764 }
1765 // attach labeled data sequences to series
1766 if( aLabeledSeqVec.empty() )
1767 xErrorBar.clear();
1768 else
1769 xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
1770 }
1771 }
1772 break;
1773 default:
1774 xErrorBar.clear();
1775 }
1776
1777 // error bar formatting
1778 if( pPrimaryBar->mxDataFmt && xErrorBar.is() )
1779 pPrimaryBar->mxDataFmt->ConvertLine( aBarProp, EXC_CHOBJTYPE_ERRORBAR );
1780 }
1781
1782 return xErrorBar;
1783}
1784
1785XclImpChSeries::XclImpChSeries( const XclImpChRoot& rRoot, sal_uInt16 nSeriesIdx ) :
1786 XclImpChRoot( rRoot ),
1787 mnGroupIdx( EXC_CHSERGROUP_NONE ),
1788 mnSeriesIdx( nSeriesIdx ),
1789 mnParentIdx( EXC_CHSERIES_INVALID ),
1790 mbLabelDeleted( false )
1791{
1792}
1793
1795{
1796 maData.mnCategType = rStrm.ReaduInt16();
1797 maData.mnValueType = rStrm.ReaduInt16();
1798 maData.mnCategCount = rStrm.ReaduInt16();
1799 maData.mnValueCount = rStrm.ReaduInt16();
1800 if( GetBiff() == EXC_BIFF8 )
1801 {
1802 maData.mnBubbleType = rStrm.ReaduInt16();
1803 maData.mnBubbleCount = rStrm.ReaduInt16();
1804 }
1805}
1806
1808{
1809 switch( rStrm.GetRecId() )
1810 {
1813 break;
1816 break;
1817 case EXC_ID_CHSERGROUP:
1818 mnGroupIdx = rStrm.ReaduInt16();
1819 break;
1820 case EXC_ID_CHSERPARENT:
1822 break;
1825 break;
1828 break;
1831 break;
1832 }
1833}
1834
1836{
1837 if (!xDataFmt)
1838 return;
1839
1840 sal_uInt16 nPointIdx = xDataFmt->GetPointPos().mnPointIdx;
1841 if (nPointIdx == EXC_CHDATAFORMAT_ALLPOINTS)
1842 {
1843 if (mxSeriesFmt)
1844 // Don't overwrite the existing format.
1845 return;
1846
1847 mxSeriesFmt = xDataFmt;
1848 if (HasParentSeries())
1849 return;
1850
1852 if (pTypeGroup)
1853 pTypeGroup->SetUsedFormatIndex(xDataFmt->GetFormatIdx());
1854
1855 return;
1856 }
1857
1858 if (nPointIdx >= EXC_CHDATAFORMAT_MAXPOINTCOUNT)
1859 // Above the max point count. Bail out.
1860 return;
1861
1862 XclImpChDataFormatMap::iterator itr = maPointFmts.lower_bound(nPointIdx);
1863 if (itr == maPointFmts.end() || maPointFmts.key_comp()(nPointIdx, itr->first))
1864 {
1865 // No object exists at this point index position. Insert it.
1866 itr = maPointFmts.insert(itr, XclImpChDataFormatMap::value_type(nPointIdx, xDataFmt));
1867 }
1868}
1869
1871{
1872 if (!xLabel)
1873 return;
1874
1875 sal_uInt16 nPointIdx = xLabel->GetPointPos().mnPointIdx;
1876 if ((nPointIdx != EXC_CHDATAFORMAT_ALLPOINTS) && (nPointIdx >= EXC_CHDATAFORMAT_MAXPOINTCOUNT))
1877 // Above the maximum allowed data points. Bail out.
1878 return;
1879
1880 XclImpChTextMap::iterator itr = maLabels.lower_bound(nPointIdx);
1881 if (itr == maLabels.end() || maLabels.key_comp()(nPointIdx, itr->first))
1882 {
1883 // No object exists at this point index position. Insert it.
1884 itr = maLabels.insert(itr, XclImpChTextMap::value_type(nPointIdx, xLabel));
1885 }
1886}
1887
1889{
1890 OSL_ENSURE( !HasParentSeries(), "XclImpChSeries::AddChildSeries - not allowed for child series" );
1891 if (&rSeries == this)
1892 {
1893 SAL_WARN("sc.filter", "self add attempt");
1894 return;
1895 }
1896
1897 /* In Excel, trend lines and error bars are stored as own series. In Calc,
1898 these are properties of the parent series. This function adds the
1899 settings of the passed series to this series. */
1900 maTrendLines.insert( maTrendLines.end(), rSeries.maTrendLines.begin(), rSeries.maTrendLines.end() );
1901 for (auto const& it : rSeries.m_ErrorBars)
1902 {
1903 m_ErrorBars.insert(std::make_pair(it.first, std::make_unique<XclImpChSerErrorBar>(*it.second)));
1904 }
1905}
1906
1908{
1909 if( HasParentSeries() )
1910 {
1911 // *** series is a child series, e.g. trend line or error bar ***
1912
1913 // create missing series format
1914 if( !mxSeriesFmt )
1916
1917 if( mxSeriesFmt )
1918 {
1919 // #i83100# set text label format, e.g. for trend line equations
1920 XclImpChTextRef xLabel;
1921 XclImpChTextMap::iterator itr = maLabels.find(EXC_CHDATAFORMAT_ALLPOINTS);
1922 if (itr != maLabels.end())
1923 xLabel = itr->second;
1924 mxSeriesFmt->SetDataLabel(xLabel);
1925 // create missing automatic formats
1926 mxSeriesFmt->UpdateTrendLineFormat();
1927 }
1928
1929 // copy series formatting to child objects
1930 for (auto const& trendLine : maTrendLines)
1931 {
1932 trendLine->SetDataFormat(mxSeriesFmt);
1933 if (mxTitleLink && mxTitleLink->HasString())
1934 {
1935 trendLine->SetTrendlineName(mxTitleLink->GetString());
1936 }
1937 }
1938 for (auto const& it : m_ErrorBars)
1939 {
1940 it.second->SetSeriesData( mxValueLink, mxSeriesFmt );
1941 }
1942 }
1943 else if( XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() )
1944 {
1945 // *** series is a regular data series ***
1946
1947 // create missing series format
1948 if( !mxSeriesFmt )
1949 {
1950 // #i51639# use a new unused format index to create series default format
1951 sal_uInt16 nFormatIdx = pTypeGroup->PopUnusedFormatIndex();
1953 }
1954
1955 // set text labels to data formats
1956 for (auto const& label : maLabels)
1957 {
1958 sal_uInt16 nPointIdx = label.first;
1959 if (nPointIdx == EXC_CHDATAFORMAT_ALLPOINTS)
1960 {
1961 if (!mxSeriesFmt)
1963 mxSeriesFmt->SetDataLabel(label.second);
1964 }
1965 else if (nPointIdx < EXC_CHDATAFORMAT_MAXPOINTCOUNT)
1966 {
1968 XclImpChDataFormatMap::iterator itr = maPointFmts.lower_bound(nPointIdx);
1969 if (itr == maPointFmts.end() || maPointFmts.key_comp()(nPointIdx, itr->first))
1970 {
1971 // No object exists at this point index position. Insert
1972 // a new one.
1974 itr = maPointFmts.insert(
1975 itr, XclImpChDataFormatMap::value_type(nPointIdx, p));
1976 }
1977 else
1978 p = itr->second;
1979 p->SetDataLabel(label.second);
1980 }
1981 }
1982
1983 // update series format (copy missing formatting from group default format)
1984 if( mxSeriesFmt )
1985 mxSeriesFmt->UpdateSeriesFormat( pTypeGroup->GetTypeInfo(), pTypeGroup->GetGroupFormat().get() );
1986
1987 // update data point formats (removes unchanged automatic formatting)
1988 for (auto const& pointFormat : maPointFmts)
1989 pointFormat.second->UpdatePointFormat( pTypeGroup->GetTypeInfo(), mxSeriesFmt.get() );
1990 }
1991}
1992
1993namespace {
1994
1996ScfPropertySet lclGetPointPropSet( Reference< XDataSeries > const & xDataSeries, sal_uInt16 nPointIdx )
1997{
1998 ScfPropertySet aPropSet;
1999 try
2000 {
2001 aPropSet.Set( xDataSeries->getDataPointByIndex( static_cast< sal_Int32 >( nPointIdx ) ) );
2002 }
2003 catch( Exception& )
2004 {
2005 OSL_FAIL( "lclGetPointPropSet - no data point property set" );
2006 }
2007 return aPropSet;
2008}
2009
2010} // namespace
2011
2012Reference< XLabeledDataSequence > XclImpChSeries::CreateValueSequence( const OUString& rValueRole ) const
2013{
2014 return lclCreateLabeledDataSequence( mxValueLink, rValueRole, mxTitleLink.get() );
2015}
2016
2017Reference< XLabeledDataSequence > XclImpChSeries::CreateCategSequence( const OUString& rCategRole ) const
2018{
2019 return lclCreateLabeledDataSequence( mxCategLink, rCategRole );
2020}
2021
2022Reference< XDataSeries > XclImpChSeries::CreateDataSeries() const
2023{
2024 Reference< XDataSeries > xDataSeries;
2025 if( const XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() )
2026 {
2027 const XclChExtTypeInfo& rTypeInfo = pTypeGroup->GetTypeInfo();
2028
2029 // create the data series object
2030 xDataSeries.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_DATASERIES ), UNO_QUERY );
2031
2032 // attach data and title sequences to series
2033 Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
2034 if( xDataSink.is() )
2035 {
2036 // create vector of all value sequences
2037 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
2038 // add Y values
2039 Reference< XLabeledDataSequence > xYValueSeq =
2041 if( xYValueSeq.is() )
2042 aLabeledSeqVec.push_back( xYValueSeq );
2043 // add X values
2044 if( !rTypeInfo.mbCategoryAxis )
2045 {
2046 Reference< XLabeledDataSequence > xXValueSeq =
2048 if( xXValueSeq.is() )
2049 aLabeledSeqVec.push_back( xXValueSeq );
2050 // add size values of bubble charts
2051 if( rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES )
2052 {
2053 Reference< XLabeledDataSequence > xSizeValueSeq =
2054 lclCreateLabeledDataSequence( mxBubbleLink, EXC_CHPROP_ROLE_SIZEVALUES, mxTitleLink.get() );
2055 if( xSizeValueSeq.is() )
2056 aLabeledSeqVec.push_back( xSizeValueSeq );
2057 }
2058 }
2059 // attach labeled data sequences to series
2060 if( !aLabeledSeqVec.empty() )
2061 xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
2062 }
2063
2064 // series formatting
2065 ScfPropertySet aSeriesProp( xDataSeries );
2066 if( mxSeriesFmt )
2067 mxSeriesFmt->Convert( aSeriesProp, rTypeInfo );
2068
2069 if (mbLabelDeleted)
2070 aSeriesProp.SetProperty(EXC_CHPROP_SHOWLEGENDENTRY, false);
2071
2072 // trend lines
2073 ConvertTrendLines( xDataSeries );
2074
2075 // error bars
2076 Reference< XPropertySet > xErrorBarX = CreateErrorBar( EXC_CHSERERR_XPLUS, EXC_CHSERERR_XMINUS );
2077 if( xErrorBarX.is() )
2078 aSeriesProp.SetProperty( EXC_CHPROP_ERRORBARX, xErrorBarX );
2079 Reference< XPropertySet > xErrorBarY = CreateErrorBar( EXC_CHSERERR_YPLUS, EXC_CHSERERR_YMINUS );
2080 if( xErrorBarY.is() )
2081 aSeriesProp.SetProperty( EXC_CHPROP_ERRORBARY, xErrorBarY );
2082
2083 // own area formatting for every data point (TODO: varying line color not supported)
2084 bool bVarPointFmt = pTypeGroup->HasVarPointFormat() && rTypeInfo.IsSeriesFrameFormat();
2086 // #i91271# always set area formatting for every point in pie/doughnut charts
2087 if (mxSeriesFmt && mxValueLink && ((bVarPointFmt && mxSeriesFmt->IsAutoArea()) || (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE)))
2088 {
2089 for( sal_uInt16 nPointIdx = 0, nPointCount = mxValueLink->GetCellCount(); nPointIdx < nPointCount; ++nPointIdx )
2090 {
2091 ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIdx );
2092 mxSeriesFmt->ConvertArea( aPointProp, bVarPointFmt ? nPointIdx : mnSeriesIdx );
2093 }
2094 }
2095
2096 // data point formatting
2097 for (auto const& pointFormat : maPointFmts)
2098 {
2099 ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, pointFormat.first );
2100 pointFormat.second->Convert( aPointProp, rTypeInfo, &aSeriesProp );
2101 }
2102 }
2103 return xDataSeries;
2104}
2105
2106void XclImpChSeries::FillAllSourceLinks( ::std::vector< ScTokenRef >& rTokens ) const
2107{
2108 if( mxValueLink )
2109 mxValueLink->FillSourceLink( rTokens );
2110 if( mxCategLink )
2111 mxCategLink->FillSourceLink( rTokens );
2112 if( mxTitleLink )
2113 mxTitleLink->FillSourceLink( rTokens );
2114 if( mxBubbleLink )
2115 mxBubbleLink->FillSourceLink( rTokens );
2116}
2117
2119{
2120 XclImpChSourceLinkRef xSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
2121 xSrcLink->ReadChSourceLink( rStrm );
2122 switch( xSrcLink->GetDestType() )
2123 {
2124 case EXC_CHSRCLINK_TITLE: mxTitleLink = xSrcLink; break;
2125 case EXC_CHSRCLINK_VALUES: mxValueLink = xSrcLink; break;
2126 case EXC_CHSRCLINK_CATEGORY: mxCategLink = xSrcLink; break;
2127 case EXC_CHSRCLINK_BUBBLES: mxBubbleLink = xSrcLink; break;
2128 }
2129}
2130
2132{
2133 // #i51639# chart stores all data formats and assigns them later to the series
2135}
2136
2138{
2139 mnParentIdx = rStrm.ReaduInt16();
2140 // index to parent series is 1-based, convert it to 0-based
2141 if( mnParentIdx > 0 )
2142 --mnParentIdx;
2143 else
2145}
2146
2148{
2149 XclImpChSerTrendLineRef xTrendLine = std::make_shared<XclImpChSerTrendLine>( GetChRoot() );
2150 xTrendLine->ReadChSerTrendLine( rStrm );
2151 maTrendLines.push_back( xTrendLine );
2152}
2153
2155{
2156 unique_ptr<XclImpChSerErrorBar> pErrorBar(new XclImpChSerErrorBar(GetChRoot()));
2157 pErrorBar->ReadChSerErrorBar(rStrm);
2158 sal_uInt8 nBarType = pErrorBar->GetBarType();
2159 m_ErrorBars.insert(std::make_pair(nBarType, std::move(pErrorBar)));
2160}
2161
2162XclImpChDataFormatRef XclImpChSeries::CreateDataFormat( sal_uInt16 nPointIdx, sal_uInt16 nFormatIdx )
2163{
2164 XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
2165 xDataFmt->SetPointPos( XclChDataPointPos( mnSeriesIdx, nPointIdx ), nFormatIdx );
2166 return xDataFmt;
2167}
2168
2169void XclImpChSeries::ConvertTrendLines( Reference< XDataSeries > const & xDataSeries ) const
2170{
2171 Reference< XRegressionCurveContainer > xRegCurveCont( xDataSeries, UNO_QUERY );
2172 if( !xRegCurveCont.is() )
2173 return;
2174
2175 for (auto const& trendLine : maTrendLines)
2176 {
2177 try
2178 {
2179 Reference< XRegressionCurve > xRegCurve = trendLine->CreateRegressionCurve();
2180 if( xRegCurve.is() )
2181 {
2182 xRegCurveCont->addRegressionCurve( xRegCurve );
2183 }
2184 }
2185 catch( Exception& )
2186 {
2187 OSL_FAIL( "XclImpChSeries::ConvertTrendLines - cannot add regression curve" );
2188 }
2189 }
2190}
2191
2192Reference< XPropertySet > XclImpChSeries::CreateErrorBar( sal_uInt8 nPosBarId, sal_uInt8 nNegBarId ) const
2193{
2194 XclImpChSerErrorBarMap::const_iterator itrPosBar = m_ErrorBars.find(nPosBarId);
2195 XclImpChSerErrorBarMap::const_iterator itrNegBar = m_ErrorBars.find(nNegBarId);
2196 XclImpChSerErrorBarMap::const_iterator itrEnd = m_ErrorBars.end();
2197 if (itrPosBar == itrEnd || itrNegBar == itrEnd)
2198 return Reference<XPropertySet>();
2199
2200 return XclImpChSerErrorBar::CreateErrorBar(itrPosBar->second.get(), itrNegBar->second.get());
2201}
2202
2204{
2205 rStrm.Ignore(2);
2206 sal_uInt16 nFlags = rStrm.ReaduInt16();
2208}
2209
2210// Chart type groups ==========================================================
2211
2213 XclImpChRoot( rRoot ),
2214 mnRecId( EXC_ID_CHUNKNOWN ),
2215 maTypeInfo( rRoot.GetChartTypeInfo( EXC_CHTYPEID_UNKNOWN ) )
2216{
2217}
2218
2220{
2221 sal_uInt16 nRecId = rStrm.GetRecId();
2222 bool bKnownType = true;
2223
2224 switch( nRecId )
2225 {
2226 case EXC_ID_CHBAR:
2229 maData.mnFlags = rStrm.ReaduInt16();
2230 break;
2231
2232 case EXC_ID_CHLINE:
2233 case EXC_ID_CHAREA:
2234 case EXC_ID_CHRADARLINE:
2235 case EXC_ID_CHRADARAREA:
2236 maData.mnFlags = rStrm.ReaduInt16();
2237 break;
2238
2239 case EXC_ID_CHPIE:
2240 maData.mnRotation = rStrm.ReaduInt16();
2241 maData.mnPieHole = rStrm.ReaduInt16();
2242 if( GetBiff() == EXC_BIFF8 )
2243 maData.mnFlags = rStrm.ReaduInt16();
2244 else
2245 maData.mnFlags = 0;
2246 break;
2247
2248 case EXC_ID_CHPIEEXT:
2249 maData.mnRotation = 0;
2250 maData.mnPieHole = 0;
2251 maData.mnFlags = 0;
2252 break;
2253
2254 case EXC_ID_CHSCATTER:
2255 if( GetBiff() == EXC_BIFF8 )
2256 {
2257 maData.mnBubbleSize = rStrm.ReaduInt16();
2258 maData.mnBubbleType = rStrm.ReaduInt16();
2259 maData.mnFlags = rStrm.ReaduInt16();
2260 }
2261 else
2262 maData.mnFlags = 0;
2263 break;
2264
2265 case EXC_ID_CHSURFACE:
2266 maData.mnFlags = rStrm.ReaduInt16();
2267 break;
2268
2269 default:
2270 bKnownType = false;
2271 }
2272
2273 if( bKnownType )
2274 mnRecId = nRecId;
2275}
2276
2277void XclImpChType::Finalize( bool bStockChart )
2278{
2279 switch( mnRecId )
2280 {
2281 case EXC_ID_CHLINE:
2282 maTypeInfo = GetChartTypeInfo( bStockChart ?
2284 break;
2285 case EXC_ID_CHBAR:
2289 break;
2290 case EXC_ID_CHPIE:
2293 break;
2294 case EXC_ID_CHSCATTER:
2298 break;
2299 default:
2301 }
2302
2303 switch( maTypeInfo.meTypeId )
2304 {
2310 break;
2311 default:;
2312 }
2313}
2314
2316{
2317 bool bStacked = false;
2319 {
2321 bStacked =
2324 break;
2326 bStacked =
2329 break;
2330 default:;
2331 }
2332 return bStacked;
2333}
2334
2336{
2337 bool bPercent = false;
2339 {
2341 bPercent =
2344 break;
2346 bPercent =
2349 break;
2350 default:;
2351 }
2352 return bPercent;
2353}
2354
2356{
2357 // radar charts disable category labels in chart type, not in CHTICK of X axis
2359}
2360
2361Reference< XCoordinateSystem > XclImpChType::CreateCoordSystem( bool b3dChart ) const
2362{
2363 // create the coordinate system object
2364 Reference< css::uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
2365 Reference< XCoordinateSystem > xCoordSystem;
2367 {
2368 if( b3dChart )
2369 xCoordSystem = css::chart2::PolarCoordinateSystem3d::create(xContext);
2370 else
2371 xCoordSystem = css::chart2::PolarCoordinateSystem2d::create(xContext);
2372 }
2373 else
2374 {
2375 if( b3dChart )
2376 xCoordSystem = css::chart2::CartesianCoordinateSystem3d::create(xContext);
2377 else
2378 xCoordSystem = css::chart2::CartesianCoordinateSystem2d::create(xContext);
2379 }
2380
2381 // swap X and Y axis
2383 {
2384 ScfPropertySet aCoordSysProp( xCoordSystem );
2385 aCoordSysProp.SetBoolProperty( EXC_CHPROP_SWAPXANDYAXIS, true );
2386 }
2387
2388 return xCoordSystem;
2389}
2390
2391Reference< XChartType > XclImpChType::CreateChartType( Reference< XDiagram > const & xDiagram, bool b3dChart ) const
2392{
2393 OUString aService = OUString::createFromAscii( maTypeInfo.mpcServiceName );
2394 Reference< XChartType > xChartType( ScfApiHelper::CreateInstance( aService ), UNO_QUERY );
2395
2396 // additional properties
2397 switch( maTypeInfo.meTypeCateg )
2398 {
2400 {
2401 ScfPropertySet aTypeProp( xChartType );
2402 Sequence< sal_Int32 > aInt32Seq{ -maData.mnOverlap, -maData.mnOverlap };
2403 aTypeProp.SetProperty( EXC_CHPROP_OVERLAPSEQ, aInt32Seq );
2404 aInt32Seq = { maData.mnGap, maData.mnGap };
2405 aTypeProp.SetProperty( EXC_CHPROP_GAPWIDTHSEQ, aInt32Seq );
2406 }
2407 break;
2409 {
2410 ScfPropertySet aTypeProp( xChartType );
2412 /* #i85166# starting angle of first pie slice. 3D pie charts use Y
2413 rotation setting in view3D element. Of-pie charts do not
2414 support pie rotation. */
2415 if( !b3dChart && (maTypeInfo.meTypeId != EXC_CHTYPEID_PIEEXT) )
2416 {
2417 ScfPropertySet aDiaProp( xDiagram );
2419 }
2420 }
2421 break;
2422 default:;
2423 }
2424
2425 return xChartType;
2426}
2427
2429{
2430 maData.mnRotation = rStrm.ReaduInt16();
2432 maData.mnEyeDist = rStrm.ReaduInt16();
2433 maData.mnRelHeight = rStrm.ReaduInt16();
2434 maData.mnRelDepth = rStrm.ReaduInt16();
2435 maData.mnDepthGap = rStrm.ReaduInt16();
2436 maData.mnFlags = rStrm.ReaduInt16();
2437}
2438
2439void XclImpChChart3d::Convert( ScfPropertySet& rPropSet, bool b3dWallChart ) const
2440{
2441 namespace cssd = ::com::sun::star::drawing;
2442
2443// #i104057# do not assert this, written by broken external generators
2444// OSL_ENSURE( ::get_flag( maData.mnFlags, EXC_CHCHART3D_HASWALLS ) == b3dWallChart, "XclImpChChart3d::Convert - wrong wall flag" );
2445
2446 sal_Int32 nRotationY = 0;
2447 sal_Int32 nRotationX = 0;
2448 sal_Int32 nPerspective = 15;
2449 bool bRightAngled = false;
2450 cssd::ProjectionMode eProjMode = cssd::ProjectionMode_PERSPECTIVE;
2451 Color aAmbientColor, aLightColor;
2452
2453 if( b3dWallChart )
2454 {
2455 // Y rotation (Excel [0..359], Chart2 [-179,180])
2456 nRotationY = NormAngle180<sal_Int32>(maData.mnRotation);
2457 // X rotation a.k.a. elevation (Excel [-90..90], Chart2 [-179,180])
2458 nRotationX = limit_cast< sal_Int32, sal_Int32 >( maData.mnElevation, -90, 90 );
2459 // perspective (Excel and Chart2 [0,100])
2460 nPerspective = limit_cast< sal_Int32, sal_Int32 >( maData.mnEyeDist, 0, 100 );
2461 // right-angled axes
2462 bRightAngled = !::get_flag( maData.mnFlags, EXC_CHCHART3D_REAL3D );
2463 // projection mode (parallel axes, if right-angled, #i90360# or if perspective is at 0%)
2464 bool bParallel = bRightAngled || (nPerspective == 0);
2465 eProjMode = bParallel ? cssd::ProjectionMode_PARALLEL : cssd::ProjectionMode_PERSPECTIVE;
2466 // ambient color (Gray 20%)
2467 aAmbientColor = Color( 204, 204, 204 );
2468 // light color (Gray 60%)
2469 aLightColor = Color( 102, 102, 102 );
2470 }
2471 else
2472 {
2473 // Y rotation not used in pie charts, but 'first pie slice angle'
2474 nRotationY = 0;
2476 // X rotation a.k.a. elevation (map Excel [10..80] to Chart2 [-80,-10])
2477 nRotationX = limit_cast< sal_Int32, sal_Int32 >( maData.mnElevation, 10, 80 ) - 90;
2478 // perspective (Excel and Chart2 [0,100])
2479 nPerspective = limit_cast< sal_Int32, sal_Int32 >( maData.mnEyeDist, 0, 100 );
2480 // no right-angled axes in pie charts, but parallel projection
2481 bRightAngled = false;
2482 eProjMode = cssd::ProjectionMode_PARALLEL;
2483 // ambient color (Gray 30%)
2484 aAmbientColor = Color( 179, 179, 179 );
2485 // light color (Gray 70%)
2486 aLightColor = Color( 76, 76, 76 );
2487 }
2488
2489 // properties
2490 rPropSet.SetProperty( EXC_CHPROP_3DRELATIVEHEIGHT, static_cast<sal_Int32>(maData.mnRelHeight / 2)); // seems to be 200%, change to 100%
2491 rPropSet.SetProperty( EXC_CHPROP_ROTATIONVERTICAL, nRotationY );
2492 rPropSet.SetProperty( EXC_CHPROP_ROTATIONHORIZONTAL, nRotationX );
2493 rPropSet.SetProperty( EXC_CHPROP_PERSPECTIVE, nPerspective );
2494 rPropSet.SetBoolProperty( EXC_CHPROP_RIGHTANGLEDAXES, bRightAngled );
2495 rPropSet.SetProperty( EXC_CHPROP_D3DSCENEPERSPECTIVE, eProjMode );
2496
2497 // light settings
2498 rPropSet.SetProperty( EXC_CHPROP_D3DSCENESHADEMODE, cssd::ShadeMode_FLAT );
2499 rPropSet.SetColorProperty( EXC_CHPROP_D3DSCENEAMBIENTCOLOR, aAmbientColor );
2502 rPropSet.SetColorProperty( EXC_CHPROP_D3DSCENELIGHTCOLOR2, aLightColor );
2503 rPropSet.SetProperty( EXC_CHPROP_D3DSCENELIGHTDIR2, cssd::Direction3D( 0.2, 0.4, 1.0 ) );
2504}
2505
2507 XclImpChRoot( rRoot )
2508{
2509}
2510
2512{
2513 rStrm >> maData.maRect;
2514 maData.mnDockMode = rStrm.ReaduInt8();
2515 maData.mnSpacing = rStrm.ReaduInt8();
2516 maData.mnFlags = rStrm.ReaduInt16();
2517
2518 // trace unsupported features
2519 if( GetTracer().IsEnabled() )
2520 {
2525 }
2526}
2527
2529{
2530 switch( rStrm.GetRecId() )
2531 {
2532 case EXC_ID_CHFRAMEPOS:
2533 mxFramePos = std::make_shared<XclImpChFramePos>();
2534 mxFramePos->ReadChFramePos( rStrm );
2535 break;
2536 case EXC_ID_CHTEXT:
2537 mxText = std::make_shared<XclImpChText>( GetChRoot() );
2538 mxText->ReadRecordGroup( rStrm );
2539 break;
2540 case EXC_ID_CHFRAME:
2541 mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_LEGEND );
2542 mxFrame->ReadRecordGroup( rStrm );
2543 break;
2544 }
2545}
2546
2548{
2549 // legend default formatting differs in OOChart and Excel, missing frame means automatic
2550 if( !mxFrame )
2551 mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_LEGEND );
2552 // Update text formatting. If mxText is empty, the passed default text is used.
2553 lclUpdateText( mxText, GetChartData().GetDefaultText( EXC_CHTEXTTYPE_LEGEND ) );
2554}
2555
2556Reference< XLegend > XclImpChLegend::CreateLegend() const
2557{
2558 Reference< XLegend > xLegend( ScfApiHelper::CreateInstance( SERVICE_CHART2_LEGEND ), UNO_QUERY );
2559 if( xLegend.is() )
2560 {
2561 ScfPropertySet aLegendProp( xLegend );
2562 aLegendProp.SetBoolProperty( EXC_CHPROP_SHOW, true );
2563
2564 // frame properties
2565 if( mxFrame )
2566 mxFrame->Convert( aLegendProp );
2567 // text properties
2568 if( mxText )
2569 mxText->ConvertFont( aLegendProp );
2570
2571 /* Legend position and size. Default positions are used only if the
2572 plot area is positioned automatically (Excel sets the plot area to
2573 manual mode, if the legend is moved or resized). With manual plot
2574 areas, Excel ignores the value in maData.mnDockMode completely. */
2575 cssc2::LegendPosition eApiPos = cssc2::LegendPosition_LINE_END;
2576 cssc::ChartLegendExpansion eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
2577 if( !GetChartData().IsManualPlotArea() ) switch( maData.mnDockMode )
2578 {
2579 case EXC_CHLEGEND_LEFT:
2580 eApiPos = cssc2::LegendPosition_LINE_START;
2581 eApiExpand = cssc::ChartLegendExpansion_HIGH;
2582 break;
2583 case EXC_CHLEGEND_RIGHT:
2584 // top-right not supported
2586 eApiPos = cssc2::LegendPosition_LINE_END;
2587 eApiExpand = cssc::ChartLegendExpansion_HIGH;
2588 break;
2589 case EXC_CHLEGEND_TOP:
2590 eApiPos = cssc2::LegendPosition_PAGE_START;
2591 eApiExpand = cssc::ChartLegendExpansion_WIDE;
2592 break;
2594 eApiPos = cssc2::LegendPosition_PAGE_END;
2595 eApiExpand = cssc::ChartLegendExpansion_WIDE;
2596 break;
2597 }
2598
2599 // no automatic position/size: try to find the correct position and size
2600 if( GetChartData().IsManualPlotArea() || maData.mnDockMode == EXC_CHLEGEND_NOTDOCKED )
2601 {
2602 const XclChFramePos* pFramePos = mxFramePos ? &mxFramePos->GetFramePosData() : nullptr;
2603
2604 /* Legend position. Only the settings from the CHFRAMEPOS record
2605 are used by Excel, the position in the CHLEGEND record will be
2606 ignored. */
2607 if( pFramePos )
2608 {
2609 RelativePosition aRelPos(
2610 CalcRelativeFromChartX( pFramePos->maRect.mnX ),
2611 CalcRelativeFromChartY( pFramePos->maRect.mnY ),
2612 css::drawing::Alignment_TOP_LEFT );
2613 aLegendProp.SetProperty( EXC_CHPROP_RELATIVEPOSITION, aRelPos );
2614 }
2615 else
2616 {
2617 // no manual position/size found, just go for the default
2618 eApiPos = cssc2::LegendPosition_LINE_END;
2619 }
2620
2621 /* Legend size. The member mnBRMode specifies whether size is
2622 automatic or changes manually. Manual size is given in points,
2623 not in chart units. */
2624 if( pFramePos && (pFramePos->mnBRMode == EXC_CHFRAMEPOS_ABSSIZE_POINTS) &&
2625 (pFramePos->maRect.mnWidth > 0) && (pFramePos->maRect.mnHeight > 0) )
2626 {
2627 eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
2628 sal_Int32 nWidthHmm = o3tl::convert(pFramePos->maRect.mnWidth, o3tl::Length::pt, o3tl::Length::mm100);
2629 sal_Int32 nHeightHmm = o3tl::convert(pFramePos->maRect.mnHeight, o3tl::Length::pt, o3tl::Length::mm100);
2630 RelativeSize aRelSize( CalcRelativeFromHmmX( nWidthHmm ), CalcRelativeFromHmmY( nHeightHmm ) );
2631 aLegendProp.SetProperty( EXC_CHPROP_RELATIVESIZE, aRelSize );
2632 }
2633 else
2634 {
2635 // automatic size: determine entry direction from flags
2637 cssc::ChartLegendExpansion_HIGH, cssc::ChartLegendExpansion_WIDE );
2638 }
2639 }
2640 aLegendProp.SetProperty( EXC_CHPROP_ANCHORPOSITION, eApiPos );
2641 aLegendProp.SetProperty( EXC_CHPROP_EXPANSION, eApiExpand );
2642 }
2643 return xLegend;
2644}
2645
2646XclImpChDropBar::XclImpChDropBar( sal_uInt16 nDropBar ) :
2647 mnDropBar( nDropBar ),
2648 mnBarDist( 0 )
2649{
2650}
2651
2653{
2654 mnBarDist = rStrm.ReaduInt16();
2655}
2656
2657void XclImpChDropBar::Convert( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const
2658{
2660 switch( mnDropBar )
2661 {
2662 case EXC_CHDROPBAR_UP: eObjType = EXC_CHOBJTYPE_WHITEDROPBAR; break;
2663 case EXC_CHDROPBAR_DOWN: eObjType = EXC_CHOBJTYPE_BLACKDROPBAR; break;
2664 }
2665 ConvertFrameBase( rRoot, rPropSet, eObjType );
2666}
2667
2669 XclImpChRoot( rRoot ),
2670 maType( rRoot ),
2671 maTypeInfo( maType.GetTypeInfo() )
2672{
2673 // Initialize unused format indexes set. At this time, all formats are unused.
2674 for( sal_uInt16 nFormatIdx = 0; nFormatIdx <= EXC_CHSERIES_MAXSERIES; ++nFormatIdx )
2675 maUnusedFormats.insert( maUnusedFormats.end(), nFormatIdx );
2676}
2677
2679{
2680 rStrm.Ignore( 16 );
2681 maData.mnFlags = rStrm.ReaduInt16();
2682 maData.mnGroupIdx = rStrm.ReaduInt16();
2683}
2684
2686{
2687 switch( rStrm.GetRecId() )
2688 {
2689 case EXC_ID_CHCHART3D:
2690 mxChart3d = std::make_shared<XclImpChChart3d>();
2691 mxChart3d->ReadChChart3d( rStrm );
2692 break;
2693 case EXC_ID_CHLEGEND:
2694 mxLegend = std::make_shared<XclImpChLegend>( GetChRoot() );
2695 mxLegend->ReadRecordGroup( rStrm );
2696 break;
2699 break;
2700 case EXC_ID_CHDROPBAR:
2702 break;
2703 case EXC_ID_CHCHARTLINE:
2705 break;
2708 break;
2709 default:
2711 }
2712}
2713
2715{
2716 // check and set valid chart type
2717 bool bStockChart =
2718 (maType.GetRecId() == EXC_ID_CHLINE) && // must be a line chart
2719 !mxChart3d && // must be a 2d chart
2720 m_ChartLines.find(EXC_CHCHARTLINE_HILO) != m_ChartLines.end() && // must contain hi-lo lines
2721 (maSeries.size() == static_cast<XclImpChSeriesVec::size_type>(HasDropBars() ? 4 : 3)); // correct series count
2722 maType.Finalize( bStockChart );
2723
2724 // extended type info
2725 maTypeInfo.Set( maType.GetTypeInfo(), static_cast< bool >(mxChart3d), false );
2726
2727 // reverse series order for some unstacked 2D chart types
2729 ::std::reverse( maSeries.begin(), maSeries.end() );
2730
2731 // update chart type group format, may depend on chart type finalized above
2732 if( mxGroupFmt )
2733 mxGroupFmt->UpdateGroupFormat( maTypeInfo );
2734}
2735
2737{
2738 if( xSeries )
2739 maSeries.push_back( xSeries );
2740 // store first inserted series separately, series order may be reversed later
2741 if( !mxFirstSeries )
2742 mxFirstSeries = xSeries;
2743}
2744
2745void XclImpChTypeGroup::SetUsedFormatIndex( sal_uInt16 nFormatIdx )
2746{
2747 maUnusedFormats.erase( nFormatIdx );
2748}
2749
2751{
2752 OSL_ENSURE( !maUnusedFormats.empty(), "XclImpChTypeGroup::PopUnusedFormatIndex - no more format indexes available" );
2753 sal_uInt16 nFormatIdx = maUnusedFormats.empty() ? 0 : *maUnusedFormats.begin();
2754 SetUsedFormatIndex( nFormatIdx );
2755 return nFormatIdx;
2756}
2757
2759{
2761 ((maTypeInfo.meVarPointMode == EXC_CHVARPOINT_MULTI) || // multiple series allowed
2762 ((maTypeInfo.meVarPointMode == EXC_CHVARPOINT_SINGLE) && // or exactly 1 series?
2763 (maSeries.size() == 1)));
2764}
2765
2767{
2768 // existence of connector lines (only in stacked bar charts)
2770 return false;
2771 XclImpChLineFormatMap::const_iterator aConLine = m_ChartLines.find(EXC_CHCHARTLINE_CONNECT);
2772 return (aConLine != m_ChartLines.end() && aConLine->second.HasLine());
2773}
2774
2776{
2777 // no automatic title for series with trendlines or error bars
2778 // pie charts always show an automatic title, even if more series exist
2779 return (mxFirstSeries && !mxFirstSeries->HasChildSeries() && (maTypeInfo.mbSingleSeriesVis || (maSeries.size() == 1))) ?
2780 mxFirstSeries->GetTitle() : OUString();
2781}
2782
2784{
2785 if( mxChart3d )
2786 mxChart3d->Convert( rPropSet, Is3dWallChart() );
2787}
2788
2789Reference< XCoordinateSystem > XclImpChTypeGroup::CreateCoordSystem() const
2790{
2792}
2793
2794Reference< XChartType > XclImpChTypeGroup::CreateChartType( Reference< XDiagram > const & xDiagram, sal_Int32 nApiAxesSetIdx ) const
2795{
2796 OSL_ENSURE( IsValidGroup(), "XclImpChTypeGroup::CreateChartType - type group without series" );
2797
2798 // create the chart type object
2799 Reference< XChartType > xChartType = maType.CreateChartType( xDiagram, Is3dChart() );
2800
2801 // bar chart connector lines
2802 if( HasConnectorLines() )
2803 {
2804 ScfPropertySet aDiaProp( xDiagram );
2805 aDiaProp.SetBoolProperty( EXC_CHPROP_CONNECTBARS, true );
2806 }
2807
2808 /* Stock chart needs special processing. Create one 'big' series with
2809 data sequences of different roles. */
2811 CreateStockSeries( xChartType, nApiAxesSetIdx );
2812 else
2813 CreateDataSeries( xChartType, nApiAxesSetIdx );
2814
2815 return xChartType;
2816}
2817
2818Reference< XLabeledDataSequence > XclImpChTypeGroup::CreateCategSequence() const
2819{
2820 Reference< XLabeledDataSequence > xLabeledSeq;
2821 // create category sequence from first visible series
2822 if( mxFirstSeries )
2823 xLabeledSeq = mxFirstSeries->CreateCategSequence( EXC_CHPROP_ROLE_CATEG );
2824 return xLabeledSeq;
2825}
2826
2828{
2829 if (m_DropBars.find(EXC_CHDROPBAR_UP) == m_DropBars.end())
2830 {
2831 unique_ptr<XclImpChDropBar> p(new XclImpChDropBar(EXC_CHDROPBAR_UP));
2832 p->ReadRecordGroup(rStrm);
2833 m_DropBars.insert(std::make_pair(EXC_CHDROPBAR_UP, std::move(p)));
2834 }
2835 else if (m_DropBars.find(EXC_CHDROPBAR_DOWN) == m_DropBars.end())
2836 {
2837 unique_ptr<XclImpChDropBar> p(new XclImpChDropBar(EXC_CHDROPBAR_DOWN));
2838 p->ReadRecordGroup(rStrm);
2839 m_DropBars.insert(std::make_pair(EXC_CHDROPBAR_DOWN, std::move(p)));
2840 }
2841}
2842
2844{
2845 sal_uInt16 nLineId = rStrm.ReaduInt16();
2846 if( (rStrm.GetNextRecId() == EXC_ID_CHLINEFORMAT) && rStrm.StartNextRecord() )
2847 {
2848 XclImpChLineFormat aLineFmt;
2849 aLineFmt.ReadChLineFormat( rStrm );
2850 m_ChartLines[ nLineId ] = aLineFmt;
2851 }
2852}
2853
2855{
2856 // global series and data point format
2857 XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
2858 xDataFmt->ReadRecordGroup( rStrm );
2859 const XclChDataPointPos& rPos = xDataFmt->GetPointPos();
2860 if( (rPos.mnSeriesIdx == 0) && (rPos.mnPointIdx == 0) &&
2861 (xDataFmt->GetFormatIdx() == EXC_CHDATAFORMAT_DEFAULT) )
2862 mxGroupFmt = xDataFmt;
2863}
2864
2865void XclImpChTypeGroup::InsertDataSeries( Reference< XChartType > const & xChartType,
2866 Reference< XDataSeries > const & xSeries, sal_Int32 nApiAxesSetIdx ) const
2867{
2868 Reference< XDataSeriesContainer > xSeriesCont( xChartType, UNO_QUERY );
2869 if( !(xSeriesCont.is() && xSeries.is()) )
2870 return;
2871
2872 // series stacking mode
2873 cssc2::StackingDirection eStacking = cssc2::StackingDirection_NO_STACKING;
2874 // stacked overrides deep-3d
2875 if( maType.IsStacked() || maType.IsPercent() )
2876 eStacking = cssc2::StackingDirection_Y_STACKING;
2877 else if( Is3dDeepChart() )
2878 eStacking = cssc2::StackingDirection_Z_STACKING;
2879
2880 // additional series properties
2881 ScfPropertySet aSeriesProp( xSeries );
2882 aSeriesProp.SetProperty( EXC_CHPROP_STACKINGDIR, eStacking );
2883 aSeriesProp.SetProperty( EXC_CHPROP_ATTAXISINDEX, nApiAxesSetIdx );
2884
2885 // insert series into container
2886 try
2887 {
2888 xSeriesCont->addDataSeries( xSeries );
2889 }
2890 catch( Exception& )
2891 {
2892 OSL_FAIL( "XclImpChTypeGroup::InsertDataSeries - cannot add data series" );
2893 }
2894}
2895
2896void XclImpChTypeGroup::CreateDataSeries( Reference< XChartType > const & xChartType, sal_Int32 nApiAxesSetIdx ) const
2897{
2898 bool bSpline = false;
2899 for (auto const& elem : maSeries)
2900 {
2901 Reference< XDataSeries > xDataSeries = elem->CreateDataSeries();
2902 InsertDataSeries( xChartType, xDataSeries, nApiAxesSetIdx );
2903 bSpline |= elem->HasSpline();
2904 }
2905 // spline - TODO: set at single series (#i66858#)
2907 {
2908 ScfPropertySet aTypeProp( xChartType );
2909 aTypeProp.SetProperty( EXC_CHPROP_CURVESTYLE, css::chart2::CurveStyle_CUBIC_SPLINES );
2910 }
2911}
2912
2913void XclImpChTypeGroup::CreateStockSeries( Reference< XChartType > const & xChartType, sal_Int32 nApiAxesSetIdx ) const
2914{
2915 // create the data series object
2916 Reference< XDataSeries > xDataSeries( ScfApiHelper::CreateInstance( SERVICE_CHART2_DATASERIES ), UNO_QUERY );
2917 Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
2918 if( !xDataSink.is() )
2919 return;
2920
2921 // create a list of data sequences from all series
2922 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
2923 OSL_ENSURE( maSeries.size() >= 3, "XclImpChTypeGroup::CreateChartType - missing stock series" );
2924 int nRoleIdx = (maSeries.size() == 3) ? 1 : 0;
2925 for( const auto& rxSeries : maSeries )
2926 {
2927 // create a data sequence with a specific role
2928 OUString aRole;
2929 switch( nRoleIdx )
2930 {
2931 case 0: aRole = EXC_CHPROP_ROLE_OPENVALUES; break;
2932 case 1: aRole = EXC_CHPROP_ROLE_HIGHVALUES; break;
2933 case 2: aRole = EXC_CHPROP_ROLE_LOWVALUES; break;
2934 case 3: aRole = EXC_CHPROP_ROLE_CLOSEVALUES; break;
2935 }
2936 Reference< XLabeledDataSequence > xDataSeq = rxSeries->CreateValueSequence( aRole );
2937 if( xDataSeq.is() )
2938 aLabeledSeqVec.push_back( xDataSeq );
2939 ++nRoleIdx;
2940 if (nRoleIdx >= 4)
2941 break;
2942 }
2943
2944 // attach labeled data sequences to series and insert series into chart type
2945 xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
2946
2947 // formatting of special stock chart elements
2948 ScfPropertySet aTypeProp( xChartType );
2951 aTypeProp.SetBoolProperty( EXC_CHPROP_SHOWHIGHLOW, true );
2952 // hi-lo line format
2953 XclImpChLineFormatMap::const_iterator aHiLoLine = m_ChartLines.find( EXC_CHCHARTLINE_HILO );
2954 if (aHiLoLine != m_ChartLines.end())
2955 {
2956 ScfPropertySet aSeriesProp( xDataSeries );
2957 aHiLoLine->second.Convert( GetChRoot(), aSeriesProp, EXC_CHOBJTYPE_HILOLINE );
2958 }
2959 // white dropbar format
2960 XclImpChDropBarMap::const_iterator itr = m_DropBars.find(EXC_CHDROPBAR_UP);
2961 Reference<XPropertySet> xWhitePropSet;
2962 if (itr != m_DropBars.end() && aTypeProp.GetProperty(xWhitePropSet, EXC_CHPROP_WHITEDAY))
2963 {
2964 ScfPropertySet aBarProp( xWhitePropSet );
2965 itr->second->Convert(GetChRoot(), aBarProp);
2966 }
2967 // black dropbar format
2968 itr = m_DropBars.find(EXC_CHDROPBAR_DOWN);
2969 Reference<XPropertySet> xBlackPropSet;
2970 if (itr != m_DropBars.end() && aTypeProp.GetProperty(xBlackPropSet, EXC_CHPROP_BLACKDAY))
2971 {
2972 ScfPropertySet aBarProp( xBlackPropSet );
2973 itr->second->Convert(GetChRoot(), aBarProp);
2974 }
2975
2976 // insert the series into the chart type object
2977 InsertDataSeries( xChartType, xDataSeries, nApiAxesSetIdx );
2978}
2979
2980// Axes =======================================================================
2981
2983 XclImpChRoot( rRoot )
2984{
2985}
2986
2988{
2989 maLabelData.mnCross = rStrm.ReaduInt16();
2990 maLabelData.mnLabelFreq = rStrm.ReaduInt16();
2991 maLabelData.mnTickFreq = rStrm.ReaduInt16();
2992 maLabelData.mnFlags = rStrm.ReaduInt16();
2993}
2994
2996{
2997 maDateData.mnMinDate = rStrm.ReaduInt16();
2998 maDateData.mnMaxDate = rStrm.ReaduInt16();
2999 maDateData.mnMajorStep = rStrm.ReaduInt16();
3000 maDateData.mnMajorUnit = rStrm.ReaduInt16();
3001 maDateData.mnMinorStep = rStrm.ReaduInt16();
3002 maDateData.mnMinorUnit = rStrm.ReaduInt16();
3003 maDateData.mnBaseUnit = rStrm.ReaduInt16();
3004 maDateData.mnCross = rStrm.ReaduInt16();
3005 maDateData.mnFlags = rStrm.ReaduInt16();
3006}
3007
3008void XclImpChLabelRange::Convert( ScfPropertySet& rPropSet, ScaleData& rScaleData, bool bMirrorOrient ) const
3009{
3010 // automatic axis type detection
3011 rScaleData.AutoDateAxis = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTODATE );
3012
3013 // the flag EXC_CHDATERANGE_DATEAXIS specifies whether this is a date axis
3015 {
3016 /* Chart2 requires axis type CATEGORY for automatic category/date axis
3017 (even if it is a date axis currently). */
3018 rScaleData.AxisType = rScaleData.AutoDateAxis ? cssc2::AxisType::CATEGORY : cssc2::AxisType::DATE;
3019 rScaleData.Scaling = css::chart2::LinearScaling::create( comphelper::getProcessComponentContext() );
3020 /* Min/max values depend on base time unit, they specify the number of
3021 days, months, or years starting from null date. */
3022 lclConvertTimeValue( GetRoot(), rScaleData.Minimum, maDateData.mnMinDate, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMIN ), maDateData.mnBaseUnit );
3023 lclConvertTimeValue( GetRoot(), rScaleData.Maximum, maDateData.mnMaxDate, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAX ), maDateData.mnBaseUnit );
3024 // increment
3025 cssc::TimeIncrement& rTimeIncrement = rScaleData.TimeIncrement;
3026 lclConvertTimeInterval( rTimeIncrement.MajorTimeInterval, maDateData.mnMajorStep, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAJOR ), maDateData.mnMajorUnit );
3027 lclConvertTimeInterval( rTimeIncrement.MinorTimeInterval, maDateData.mnMinorStep, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMINOR ), maDateData.mnMinorUnit );
3028 // base unit
3030 rTimeIncrement.TimeResolution.clear();
3031 else
3032 rTimeIncrement.TimeResolution <<= lclGetApiTimeUnit( maDateData.mnBaseUnit );
3033 }
3034 else
3035 {
3036 // do not overlap text unless all labels are visible
3038 // do not break text into several lines unless all labels are visible
3040 // do not stagger labels in two lines
3041 rPropSet.SetProperty( EXC_CHPROP_ARRANGEORDER, cssc::ChartAxisArrangeOrderType_SIDE_BY_SIDE );
3042 }
3043
3044 // reverse order
3045 bool bReverse = ::get_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_REVERSE ) != bMirrorOrient;
3046 rScaleData.Orientation = bReverse ? cssc2::AxisOrientation_REVERSE : cssc2::AxisOrientation_MATHEMATICAL;
3047
3048 //TODO #i58731# show n-th category
3049}
3050
3051void XclImpChLabelRange::ConvertAxisPosition( ScfPropertySet& rPropSet, bool b3dChart ) const
3052{
3053 /* Crossing mode (max-cross flag overrides other crossing settings). Excel
3054 does not move the Y axis in 3D charts, regardless of actual settings.
3055 But: the Y axis has to be moved to "end", if the X axis is mirrored,
3056 to keep it at the left end of the chart. */
3058 cssc::ChartAxisPosition eAxisPos = bMaxCross ? cssc::ChartAxisPosition_END : cssc::ChartAxisPosition_VALUE;
3059 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERPOSITION, eAxisPos );
3060
3061 // crossing position (depending on axis type text/date)
3063 {
3065 /* Crossing position value depends on base time unit, it specifies the
3066 number of days, months, or years from null date. Note that Excel
3067 2007/2010 write broken BIFF8 files, they always stores the number
3068 of days regardless of the base time unit (and they are reading it
3069 the same way, thus wrongly displaying files written by Excel
3070 97-2003). This filter sticks to the correct behaviour of Excel
3071 97-2003. */
3072 double fCrossingPos = bAutoCross ? 1.0 : lclGetSerialDay( GetRoot(), maDateData.mnCross, maDateData.mnBaseUnit );
3073 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
3074 }
3075 else
3076 {
3077 double fCrossingPos = b3dChart ? 1.0 : maLabelData.mnCross;
3078 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
3079 }
3080}
3081
3083 XclImpChRoot( rRoot )
3084{
3085}
3086
3088{
3094 maData.mnFlags = rStrm.ReaduInt16();
3095}
3096
3097void XclImpChValueRange::Convert( ScaleData& rScaleData, bool bMirrorOrient ) const
3098{
3099 // scaling algorithm
3100 const bool bLogScale = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE );
3101 if( bLogScale )
3102 rScaleData.Scaling = css::chart2::LogarithmicScaling::create( comphelper::getProcessComponentContext() );
3103 else
3104 rScaleData.Scaling = css::chart2::LinearScaling::create( comphelper::getProcessComponentContext() );
3105
3106 // min/max
3107 lclSetExpValueOrClearAny( rScaleData.Minimum, maData.mfMin, bLogScale, ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMIN ) );
3108 lclSetExpValueOrClearAny( rScaleData.Maximum, maData.mfMax, bLogScale, ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAX ) );
3109
3110 // increment
3113 // major increment
3114 IncrementData& rIncrementData = rScaleData.IncrementData;
3115 lclSetValueOrClearAny( rIncrementData.Distance, maData.mfMajorStep, bAutoMajor );
3116 // minor increment
3117 Sequence< SubIncrement >& rSubIncrementSeq = rIncrementData.SubIncrements;
3118 rSubIncrementSeq.realloc( 1 );
3119 Any& rIntervalCount = rSubIncrementSeq.getArray()[ 0 ].IntervalCount;
3120 rIntervalCount.clear();
3121 if( bLogScale )
3122 {
3123 if( !bAutoMinor )
3124 rIntervalCount <<= sal_Int32( 9 );
3125 }
3126 else if( !bAutoMajor && !bAutoMinor && (0.0 < maData.mfMinorStep) && (maData.mfMinorStep <= maData.mfMajorStep) )
3127 {
3128 double fCount = maData.mfMajorStep / maData.mfMinorStep + 0.5;
3129 if( (1.0 <= fCount) && (fCount < 1001.0) )
3130 rIntervalCount <<= static_cast< sal_Int32 >( fCount );
3131 }
3132 else if( bAutoMinor )
3133 {
3134 // tdf#114168 If minor unit is not set then set interval to 5, as MS Excel do.
3135 rIntervalCount <<= static_cast< sal_Int32 >( 5 );
3136 }
3137
3138 // reverse order
3139 bool bReverse = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_REVERSE ) != bMirrorOrient;
3140 rScaleData.Orientation = bReverse ? cssc2::AxisOrientation_REVERSE : cssc2::AxisOrientation_MATHEMATICAL;
3141}
3142
3144{
3148
3149 // crossing mode (max-cross flag overrides other crossing settings)
3150 cssc::ChartAxisPosition eAxisPos = bMaxCross ? cssc::ChartAxisPosition_END : cssc::ChartAxisPosition_VALUE;
3151 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERPOSITION, eAxisPos );
3152
3153 // crossing position
3154 double fCrossingPos = bAutoCross ? 0.0 : maData.mfCross;
3155 if( bLogScale ) fCrossingPos = pow( 10.0, fCrossingPos );
3156 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
3157}
3158
3159namespace {
3160
3161sal_Int32 lclGetApiTickmarks( sal_uInt8 nXclTickPos )
3162{
3163 using namespace ::com::sun::star::chart2::TickmarkStyle;
3164 sal_Int32 nApiTickmarks = css::chart2::TickmarkStyle::NONE;
3165 ::set_flag( nApiTickmarks, INNER, ::get_flag( nXclTickPos, EXC_CHTICK_INSIDE ) );
3166 ::set_flag( nApiTickmarks, OUTER, ::get_flag( nXclTickPos, EXC_CHTICK_OUTSIDE ) );
3167 return nApiTickmarks;
3168}
3169
3170cssc::ChartAxisLabelPosition lclGetApiLabelPosition( sal_Int8 nXclLabelPos )
3171{
3172 using namespace ::com::sun::star::chart;
3173 switch( nXclLabelPos )
3174 {
3175 case EXC_CHTICK_LOW: return ChartAxisLabelPosition_OUTSIDE_START;
3176 case EXC_CHTICK_HIGH: return ChartAxisLabelPosition_OUTSIDE_END;
3177 case EXC_CHTICK_NEXT: return ChartAxisLabelPosition_NEAR_AXIS;
3178 }
3179 return ChartAxisLabelPosition_NEAR_AXIS;
3180}
3181
3182} // namespace
3183
3185 XclImpChRoot( rRoot )
3186{
3187}
3188
3190{
3191 maData.mnMajor = rStrm.ReaduInt8();
3192 maData.mnMinor = rStrm.ReaduInt8();
3193 maData.mnLabelPos = rStrm.ReaduInt8();
3194 maData.mnBackMode = rStrm.ReaduInt8();
3195 rStrm.Ignore( 16 );
3197 maData.mnFlags = rStrm.ReaduInt16();
3198
3199 if( GetBiff() == EXC_BIFF8 )
3200 {
3201 // BIFF8: index into palette used instead of RGB data
3202 maData.maTextColor = GetPalette().GetColor( rStrm.ReaduInt16() );
3203 // rotation
3204 maData.mnRotation = rStrm.ReaduInt16();
3205 }
3206 else
3207 {
3208 // BIFF2-BIFF7: get rotation from text orientation
3209 sal_uInt8 nOrient = ::extract_value< sal_uInt8 >( maData.mnFlags, 2, 3 );
3211 }
3212}
3213
3215{
3217}
3218
3220{
3221 /* n#720443: Ignore auto-rotation if there is a suggested rotation.
3222 * Better fix would be to improve our axis auto rotation algorithm.
3223 */
3225 return maData.mnRotation;
3227}
3228
3230{
3231 rPropSet.SetProperty( EXC_CHPROP_MAJORTICKS, lclGetApiTickmarks( maData.mnMajor ) );
3232 rPropSet.SetProperty( EXC_CHPROP_MINORTICKS, lclGetApiTickmarks( maData.mnMinor ) );
3233 rPropSet.SetProperty( EXC_CHPROP_LABELPOSITION, lclGetApiLabelPosition( maData.mnLabelPos ) );
3234 rPropSet.SetProperty( EXC_CHPROP_MARKPOSITION, cssc::ChartAxisMarkPosition_AT_AXIS );
3235}
3236
3237XclImpChAxis::XclImpChAxis( const XclImpChRoot& rRoot, sal_uInt16 nAxisType ) :
3238 XclImpChRoot( rRoot ),
3239 mnNumFmtIdx( EXC_FORMAT_NOTFOUND )
3240{
3241 maData.mnType = nAxisType;
3242}
3243
3245{
3246 maData.mnType = rStrm.ReaduInt16();
3247}
3248
3250{
3251 switch( rStrm.GetRecId() )
3252 {
3254 mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
3255 mxLabelRange->ReadChLabelRange( rStrm );
3256 break;
3257 case EXC_ID_CHDATERANGE:
3258 if( !mxLabelRange )
3259 mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
3260 mxLabelRange->ReadChDateRange( rStrm );
3261 break;
3263 mxValueRange = std::make_shared<XclImpChValueRange>( GetChRoot() );
3264 mxValueRange->ReadChValueRange( rStrm );
3265 break;
3266 case EXC_ID_CHFORMAT:
3267 mnNumFmtIdx = rStrm.ReaduInt16();
3268 break;
3269 case EXC_ID_CHTICK:
3270 mxTick = std::make_shared<XclImpChTick>( GetChRoot() );
3271 mxTick->ReadChTick( rStrm );
3272 break;
3273 case EXC_ID_CHFONT:
3274 mxFont = std::make_shared<XclImpChFont>();
3275 mxFont->ReadChFont( rStrm );
3276 break;
3277 case EXC_ID_CHAXISLINE:
3279 break;
3280 }
3281}
3282
3284{
3285 // add default scaling, needed e.g. to adjust rotation direction of pie and radar charts
3286 if( !mxLabelRange )
3287 mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
3288 if( !mxValueRange )
3289 mxValueRange = std::make_shared<XclImpChValueRange>( GetChRoot() );
3290 // remove invisible grid lines completely
3291 if( mxMajorGrid && !mxMajorGrid->HasLine() )
3292 mxMajorGrid.clear();
3293 if( mxMinorGrid && !mxMinorGrid->HasLine() )
3294 mxMinorGrid.clear();
3295 // default tick settings different in OOChart and Excel
3296 if( !mxTick )
3297 mxTick = std::make_shared<XclImpChTick>( GetChRoot() );
3298 // #i4140# different default axis line color
3299 if( !mxAxisLine )
3300 {
3301 XclChLineFormat aLineFmt;
3302 // set "show axis" flag, default if line format record is missing
3304 mxAxisLine = new XclImpChLineFormat( aLineFmt );
3305 }
3306 // add wall/floor frame for 3d charts
3307 if( !mxWallFrame )
3309}
3310
3312{
3313 return mxFont ? mxFont->GetFontIndex() : EXC_FONT_NOTFOUND;
3314}
3315
3317{
3318 return mxTick ? mxTick->GetFontColor() : GetFontAutoColor();
3319}
3320
3322{
3323 return mxTick ? mxTick->GetRotation() : EXC_CHART_AUTOROTATION;
3324}
3325
3326Reference< XAxis > XclImpChAxis::CreateAxis( const XclImpChTypeGroup& rTypeGroup, const XclImpChAxis* pCrossingAxis ) const
3327{
3328 // create the axis object (always)
3329 Reference< XAxis > xAxis( ScfApiHelper::CreateInstance( SERVICE_CHART2_AXIS ), UNO_QUERY );
3330 if( xAxis.is() )
3331 {
3332 ScfPropertySet aAxisProp( xAxis );
3333 // #i58688# axis enabled
3334 aAxisProp.SetBoolProperty( EXC_CHPROP_SHOW, !mxAxisLine || mxAxisLine->IsShowAxis() );
3335
3336 // axis line properties
3337 if( mxAxisLine )
3338 mxAxisLine->Convert( GetChRoot(), aAxisProp, EXC_CHOBJTYPE_AXISLINE );
3339 // axis ticks properties
3340 if( mxTick )
3341 mxTick->Convert( aAxisProp );
3342
3343 // axis caption text --------------------------------------------------
3344
3345 // radar charts disable their category labels via chart type, not via axis
3346 bool bHasLabels = (!mxTick || mxTick->HasLabels()) &&
3347 ((GetAxisType() != EXC_CHAXIS_X) || rTypeGroup.HasCategoryLabels());
3348 aAxisProp.SetBoolProperty( EXC_CHPROP_DISPLAYLABELS, bHasLabels );
3349 if( bHasLabels )
3350 {
3351 // font settings from CHFONT record or from default text
3352 if( mxFont )
3353 ConvertFontBase( GetChRoot(), aAxisProp );
3354 else if( const XclImpChText* pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_AXISLABEL ) )
3355 pDefText->ConvertFont( aAxisProp );
3356 // label text rotation
3357 ConvertRotationBase( aAxisProp, true );
3358 // number format
3359 bool bLinkNumberFmtToSource = true;
3361 {
3362 sal_uInt32 nScNumFmt = GetNumFmtBuffer().GetScFormat( mnNumFmtIdx );
3363 if( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND )
3364 {
3365 aAxisProp.SetProperty( EXC_CHPROP_NUMBERFORMAT, static_cast< sal_Int32 >( nScNumFmt ) );
3366 bLinkNumberFmtToSource = false;
3367 }
3368 }
3369
3370 aAxisProp.SetProperty( EXC_CHPROP_NUMBERFORMAT_LINKSRC, bLinkNumberFmtToSource );
3371 }
3372
3373 // axis scaling and increment -----------------------------------------
3374
3375 const XclChExtTypeInfo& rTypeInfo = rTypeGroup.GetTypeInfo();
3376 ScaleData aScaleData = xAxis->getScaleData();
3377 // set axis type
3378 switch( GetAxisType() )
3379 {
3380 case EXC_CHAXIS_X:
3381 if( rTypeInfo.mbCategoryAxis )
3382 {
3383 aScaleData.AxisType = cssc2::AxisType::CATEGORY;
3384 aScaleData.Categories = rTypeGroup.CreateCategSequence();
3385 }
3386 else
3387 aScaleData.AxisType = cssc2::AxisType::REALNUMBER;
3388 break;
3389 case EXC_CHAXIS_Y:
3390 aScaleData.AxisType = rTypeGroup.IsPercent() ?
3391 cssc2::AxisType::PERCENT : cssc2::AxisType::REALNUMBER;
3392 break;
3393 case EXC_CHAXIS_Z:
3394 aScaleData.AxisType = cssc2::AxisType::SERIES;
3395 break;
3396 }
3397 // axis scaling settings, dependent on axis type
3398 switch( aScaleData.AxisType )
3399 {
3400 case cssc2::AxisType::CATEGORY:
3401 case cssc2::AxisType::SERIES:
3402 // #i71684# radar charts have reversed rotation direction
3403 if (mxLabelRange)
3404 mxLabelRange->Convert( aAxisProp, aScaleData, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR );
3405 else
3406 SAL_WARN("sc.filter", "missing LabelRange");
3407 break;
3408 case cssc2::AxisType::REALNUMBER:
3410 // #i85167# pie/donut charts have reversed rotation direction (at Y axis!)
3411 if (mxValueRange)
3412 mxValueRange->Convert( aScaleData, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE );
3413 else
3414 SAL_WARN("sc.filter", "missing ValueRange");
3415 break;
3416 default:
3417 OSL_FAIL( "XclImpChAxis::CreateAxis - unknown axis type" );
3418 }
3419
3420 /* Do not set a value to the Origin member anymore (will be done via
3421 new axis properties 'CrossoverPosition' and 'CrossoverValue'). */
3422 aScaleData.Origin.clear();
3423
3424 // write back
3425 xAxis->setScaleData( aScaleData );
3426
3427 // grid ---------------------------------------------------------------
3428
3429 // main grid
3430 ScfPropertySet aGridProp( xAxis->getGridProperties() );
3431 aGridProp.SetBoolProperty( EXC_CHPROP_SHOW, static_cast<bool>(mxMajorGrid) );
3432 if( mxMajorGrid )
3433 mxMajorGrid->Convert( GetChRoot(), aGridProp, EXC_CHOBJTYPE_GRIDLINE );
3434 // sub grid
3435 Sequence< Reference< XPropertySet > > aSubGridPropSeq = xAxis->getSubGridProperties();
3436 if( aSubGridPropSeq.hasElements() )
3437 {
3438 ScfPropertySet aSubGridProp( aSubGridPropSeq[ 0 ] );
3439 aSubGridProp.SetBoolProperty( EXC_CHPROP_SHOW, static_cast<bool>(mxMinorGrid) );
3440 if( mxMinorGrid )
3441 mxMinorGrid->Convert( GetChRoot(), aSubGridProp, EXC_CHOBJTYPE_GRIDLINE );
3442 }
3443
3444 // position of crossing axis ------------------------------------------
3445
3446 if( pCrossingAxis )
3447 pCrossingAxis->ConvertAxisPosition( aAxisProp, rTypeGroup );
3448 }
3449 return xAxis;
3450}
3451
3453{
3454 // #i71810# walls and floor in 3D charts use the CHPICFORMAT record for bitmap mode
3455 if( mxWallFrame )
3456 mxWallFrame->Convert( rPropSet, true );
3457}
3458
3460{
3461 if( ((GetAxisType() == EXC_CHAXIS_X) && rTypeGroup.GetTypeInfo().mbCategoryAxis) || (GetAxisType() == EXC_CHAXIS_Z) )
3462 {
3463 if (mxLabelRange)
3464 mxLabelRange->ConvertAxisPosition( rPropSet, rTypeGroup.Is3dChart() );
3465 else
3466 SAL_WARN("sc.filter", "missing LabelRange");
3467 }
3468 else
3469 {
3470 if (mxValueRange)
3471 mxValueRange->ConvertAxisPosition( rPropSet );
3472 else
3473 SAL_WARN("sc.filter", "missing ValueRange");
3474 }
3475}
3476
3478{
3479 XclImpChLineFormatRef* pxLineFmt = nullptr;
3480 bool bWallFrame = false;
3481 switch( rStrm.ReaduInt16() )
3482 {
3483 case EXC_CHAXISLINE_AXISLINE: pxLineFmt = &mxAxisLine; break;
3484 case EXC_CHAXISLINE_MAJORGRID: pxLineFmt = &mxMajorGrid; break;
3485 case EXC_CHAXISLINE_MINORGRID: pxLineFmt = &mxMinorGrid; break;
3486 case EXC_CHAXISLINE_WALLS: bWallFrame = true; break;
3487 }
3488 if( bWallFrame )
3490
3491 bool bLoop = pxLineFmt || bWallFrame;
3492 while( bLoop )
3493 {
3494 sal_uInt16 nRecId = rStrm.GetNextRecId();
3495 bLoop = ((nRecId == EXC_ID_CHLINEFORMAT) ||
3496 (nRecId == EXC_ID_CHAREAFORMAT) ||
3497 (nRecId == EXC_ID_CHESCHERFORMAT))
3498 && rStrm.StartNextRecord();
3499 if( bLoop )
3500 {
3501 if( pxLineFmt && (nRecId == EXC_ID_CHLINEFORMAT) )
3502 {
3503 (*pxLineFmt) = new XclImpChLineFormat();
3504 (*pxLineFmt)->ReadChLineFormat( rStrm );
3505 }
3506 else if( bWallFrame && mxWallFrame )
3507 {
3508 mxWallFrame->ReadSubRecord( rStrm );
3509 }
3510 }
3511 }
3512}
3513
3515{
3516 switch( GetAxisType() )
3517 {
3518 case EXC_CHAXIS_X:
3519 mxWallFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_WALL3D );
3520 break;
3521 case EXC_CHAXIS_Y:
3522 mxWallFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_FLOOR3D );
3523 break;
3524 default:
3525 mxWallFrame.reset();
3526 }
3527}
3528
3529XclImpChAxesSet::XclImpChAxesSet( const XclImpChRoot& rRoot, sal_uInt16 nAxesSetId ) :
3530 XclImpChRoot( rRoot )
3531{
3532 maData.mnAxesSetId = nAxesSetId;
3533}
3534
3536{
3537 maData.mnAxesSetId = rStrm.ReaduInt16();
3538 rStrm >> maData.maRect;
3539}
3540
3542{
3543 switch( rStrm.GetRecId() )
3544 {
3545 case EXC_ID_CHFRAMEPOS:
3546 mxFramePos = std::make_shared<XclImpChFramePos>();
3547 mxFramePos->ReadChFramePos( rStrm );
3548 break;
3549 case EXC_ID_CHAXIS:
3550 ReadChAxis( rStrm );
3551 break;
3552 case EXC_ID_CHTEXT:
3553 ReadChText( rStrm );
3554 break;
3555 case EXC_ID_CHPLOTFRAME:
3557 break;
3558 case EXC_ID_CHTYPEGROUP:
3560 break;
3561 }
3562}
3563
3565{
3566 if( IsValidAxesSet() )
3567 {
3568 // finalize chart type groups, erase empty groups without series
3569 XclImpChTypeGroupMap aValidGroups;
3570 for (auto const& typeGroup : maTypeGroups)
3571 {
3572 XclImpChTypeGroupRef xTypeGroup = typeGroup.second;
3573 xTypeGroup->Finalize();
3574 if( xTypeGroup->IsValidGroup() )
3575 aValidGroups.emplace(typeGroup.first, xTypeGroup);
3576 }
3577 maTypeGroups.swap( aValidGroups );
3578 }
3579
3580 // invalid chart type groups are deleted now, check again with IsValidAxesSet()
3581 if( !IsValidAxesSet() )
3582 return;
3583
3584 // always create missing axis objects
3585 if( !mxXAxis )
3586 mxXAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_X );
3587 if( !mxYAxis )
3588 mxYAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_Y );
3589 if( !mxZAxis && GetFirstTypeGroup()->Is3dDeepChart() )
3590 mxZAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_Z );
3591
3592 // finalize axes
3593 if( mxXAxis ) mxXAxis->Finalize();
3594 if( mxYAxis ) mxYAxis->Finalize();
3595 if( mxZAxis ) mxZAxis->Finalize();
3596
3597 // finalize axis titles
3599 OUString aAutoTitle(ScResId(STR_AXISTITLE));
3600 lclFinalizeTitle( mxXAxisTitle, pDefText, aAutoTitle );
3601 lclFinalizeTitle( mxYAxisTitle, pDefText, aAutoTitle );
3602 lclFinalizeTitle( mxZAxisTitle, pDefText, aAutoTitle );
3603
3604 // #i47745# missing plot frame -> invisible border and area
3605 if( !mxPlotFrame )
3606 mxPlotFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_PLOTFRAME );
3607}
3608
3610{
3611 XclImpChTypeGroupMap::const_iterator itr = maTypeGroups.find(nGroupIdx);
3612 return itr == maTypeGroups.end() ? XclImpChTypeGroupRef() : itr->second;
3613}
3614
3616{
3617 XclImpChTypeGroupRef xTypeGroup;
3618 if( !maTypeGroups.empty() )
3619 xTypeGroup = maTypeGroups.begin()->second;
3620 return xTypeGroup;
3621}
3622
3624{
3625 XclImpChLegendRef xLegend;
3626 for( const auto& rEntry : maTypeGroups )
3627 {
3628 xLegend = rEntry.second->GetLegend();
3629 if (xLegend)
3630 break;
3631 }
3632 return xLegend;
3633}
3634
3636{
3637 return (maTypeGroups.size() == 1) ? maTypeGroups.begin()->second->GetSingleSeriesTitle() : OUString();
3638}
3639
3640void XclImpChAxesSet::Convert( Reference< XDiagram > const & xDiagram ) const
3641{
3642 if( !(IsValidAxesSet() && xDiagram.is()) )
3643 return;
3644
3645 // diagram background formatting
3647 ConvertBackground( xDiagram );
3648
3649 // create the coordinate system, this inserts all chart types and series
3650 Reference< XCoordinateSystem > xCoordSystem = CreateCoordSystem( xDiagram );
3651 if( !xCoordSystem.is() )
3652 return;
3653
3654 // insert coordinate system, if not already done
3655 try
3656 {
3657 Reference< XCoordinateSystemContainer > xCoordSystemCont( xDiagram, UNO_QUERY_THROW );
3658 Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
3659 if( !aCoordSystems.hasElements() )
3660 xCoordSystemCont->addCoordinateSystem( xCoordSystem );
3661 }
3662 catch( Exception& )
3663 {
3664 OSL_FAIL( "XclImpChAxesSet::Convert - cannot insert coordinate system" );
3665 }
3666
3667 // create the axes with grids and axis titles and insert them into the diagram
3668 ConvertAxis( mxXAxis, mxXAxisTitle, xCoordSystem, mxYAxis.get() );
3669 ConvertAxis( mxYAxis, mxYAxisTitle, xCoordSystem, mxXAxis.get() );
3670 ConvertAxis( mxZAxis, mxZAxisTitle, xCoordSystem, nullptr );
3671}
3672
3674{
3675 if( mxXAxisTitle )
3677 if( mxYAxisTitle )
3679 if( mxZAxisTitle )
3681}
3682
3684{
3685 XclImpChAxisRef xAxis = std::make_shared<XclImpChAxis>( GetChRoot() );
3686 xAxis->ReadRecordGroup( rStrm );
3687
3688 switch( xAxis->GetAxisType() )
3689 {
3690 case EXC_CHAXIS_X: mxXAxis = xAxis; break;
3691 case EXC_CHAXIS_Y: mxYAxis = xAxis; break;
3692 case EXC_CHAXIS_Z: mxZAxis = xAxis; break;
3693 }
3694}
3695
3697{
3698 XclImpChTextRef xText = std::make_shared<XclImpChText>( GetChRoot() );
3699 xText->ReadRecordGroup( rStrm );
3700
3701 switch( xText->GetLinkTarget() )
3702 {
3703 case EXC_CHOBJLINK_XAXIS: mxXAxisTitle = xText; break;
3704 case EXC_CHOBJLINK_YAXIS: mxYAxisTitle = xText; break;
3705 case EXC_CHOBJLINK_ZAXIS: mxZAxisTitle = xText; break;
3706 }
3707}
3708
3710{
3711 if( (rStrm.GetNextRecId() == EXC_ID_CHFRAME) && rStrm.StartNextRecord() )
3712 {
3713 mxPlotFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_PLOTFRAME );
3714 mxPlotFrame->ReadRecordGroup( rStrm );
3715 }
3716}
3717
3719{
3720 XclImpChTypeGroupRef xTypeGroup = std::make_shared<XclImpChTypeGroup>( GetChRoot() );
3721 xTypeGroup->ReadRecordGroup( rStrm );
3722 sal_uInt16 nGroupIdx = xTypeGroup->GetGroupIdx();
3723 XclImpChTypeGroupMap::iterator itr = maTypeGroups.lower_bound(nGroupIdx);
3724 if (itr != maTypeGroups.end() && !maTypeGroups.key_comp()(nGroupIdx, itr->first))
3725 // Overwrite the existing element.
3726 itr->second = xTypeGroup;
3727 else
3728 maTypeGroups.insert(
3729 itr, XclImpChTypeGroupMap::value_type(nGroupIdx, xTypeGroup));
3730}
3731
3732Reference< XCoordinateSystem > XclImpChAxesSet::CreateCoordSystem( Reference< XDiagram > const & xDiagram ) const
3733{
3734 Reference< XCoordinateSystem > xCoordSystem;
3735
3736 /* Try to get existing coordinate system. For now, all series from primary
3737 and secondary axes sets are inserted into one coordinate system. Later,
3738 this should be changed to use one coordinate system for each axes set. */
3739 Reference< XCoordinateSystemContainer > xCoordSystemCont( xDiagram, UNO_QUERY );
3740 if( xCoordSystemCont.is() )
3741 {
3742 Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
3743 OSL_ENSURE( aCoordSystems.getLength() <= 1, "XclImpChAxesSet::CreateCoordSystem - too many existing coordinate systems" );
3744 if( aCoordSystems.hasElements() )
3745 xCoordSystem = aCoordSystems[ 0 ];
3746 }
3747
3748 // create the coordinate system according to the first chart type
3749 if( !xCoordSystem.is() )
3750 {
3752 if( xTypeGroup )
3753 {
3754 xCoordSystem = xTypeGroup->CreateCoordSystem();
3755 // convert 3d chart settings
3756 ScfPropertySet aDiaProp( xDiagram );
3757 xTypeGroup->ConvertChart3d( aDiaProp );
3758 }
3759 }
3760
3761 /* Create XChartType objects for all chart type groups. Each group will
3762 add its series to the data provider attached to the chart document. */
3763 Reference< XChartTypeContainer > xChartTypeCont( xCoordSystem, UNO_QUERY );
3764 if( xChartTypeCont.is() )
3765 {
3766 sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
3767 for( const auto& rEntry : maTypeGroups )
3768 {
3769 try
3770 {
3771 Reference< XChartType > xChartType = rEntry.second->CreateChartType( xDiagram, nApiAxesSetIdx );
3772 if( xChartType.is() )
3773 xChartTypeCont->addChartType( xChartType );
3774 }
3775 catch( Exception& )
3776 {
3777 OSL_FAIL( "XclImpChAxesSet::CreateCoordSystem - cannot add chart type" );
3778 }
3779 }
3780 }
3781
3782 return xCoordSystem;
3783}
3784
3786 XclImpChAxisRef const & xChAxis, XclImpChTextRef const & xChAxisTitle,
3787 Reference< XCoordinateSystem > const & xCoordSystem, const XclImpChAxis* pCrossingAxis ) const
3788{
3789 if( !xChAxis )
3790 return;
3791
3792 // create and attach the axis object
3793 Reference< XAxis > xAxis = CreateAxis( *xChAxis, pCrossingAxis );
3794 if( !xAxis.is() )
3795 return;
3796
3797 // create and attach the axis title
3798 if( xChAxisTitle ) try
3799 {
3800 Reference< XTitled > xTitled( xAxis, UNO_QUERY_THROW );
3801 Reference< XTitle > xTitle( xChAxisTitle->CreateTitle(), UNO_SET_THROW );
3802 xTitled->setTitleObject( xTitle );
3803 }
3804 catch( Exception& )
3805 {
3806 OSL_FAIL( "XclImpChAxesSet::ConvertAxis - cannot set axis title" );
3807 }
3808
3809 // insert axis into coordinate system
3810 try
3811 {
3812 sal_Int32 nApiAxisDim = xChAxis->GetApiAxisDimension();
3813 sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
3814 xCoordSystem->setAxisByDimension( nApiAxisDim, xAxis, nApiAxesSetIdx );
3815 }
3816 catch( Exception& )
3817 {
3818 OSL_FAIL( "XclImpChAxesSet::ConvertAxis - cannot set axis" );
3819 }
3820}
3821
3822Reference< XAxis > XclImpChAxesSet::CreateAxis( const XclImpChAxis& rChAxis, const XclImpChAxis* pCrossingAxis ) const
3823{
3824 Reference< XAxis > xAxis;
3825 if( const XclImpChTypeGroup* pTypeGroup = GetFirstTypeGroup().get() )
3826 xAxis = rChAxis.CreateAxis( *pTypeGroup, pCrossingAxis );
3827 return xAxis;
3828}
3829
3830void XclImpChAxesSet::ConvertBackground( Reference< XDiagram > const & xDiagram ) const
3831{
3833 if( xTypeGroup && xTypeGroup->Is3dWallChart() )
3834 {
3835 // wall/floor formatting (3D charts)
3836 if( mxXAxis )
3837 {
3838 ScfPropertySet aWallProp( xDiagram->getWall() );
3839 mxXAxis->ConvertWall( aWallProp );
3840 }
3841 if( mxYAxis )
3842 {
3843 ScfPropertySet aFloorProp( xDiagram->getFloor() );
3844 mxYAxis->ConvertWall( aFloorProp );
3845 }
3846 }
3847 else if( mxPlotFrame )
3848 {
3849 // diagram background formatting
3850 ScfPropertySet aWallProp( xDiagram->getWall() );
3851 mxPlotFrame->Convert( aWallProp );
3852 }
3853}
3854
3855// The chart object ===========================================================
3856
3858 XclImpChRoot( rRoot, *this )
3859{
3860 mxPrimAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_PRIMARY );
3861 mxSecnAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_SECONDARY );
3862}
3863
3865{
3866}
3867
3869{
3870 // coordinates are stored as 16.16 fixed point
3871 rStrm >> maRect;
3872}
3873
3875{
3876 switch( rStrm.GetRecId() )
3877 {
3878 case EXC_ID_CHFRAME:
3879 mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
3880 mxFrame->ReadRecordGroup( rStrm );
3881 break;
3882 case EXC_ID_CHSERIES:
3884 break;
3887 break;
3890 break;
3891 case EXC_ID_CHAXESSET:
3893 break;
3894 case EXC_ID_CHTEXT:
3895 ReadChText( rStrm );
3896 break;
3897 case EXC_ID_CHEND:
3898 Finalize(); // finalize the entire chart object
3899 break;
3900 }
3901}
3902
3904{
3905 sal_uInt16 nTextId = rStrm.ReaduInt16();
3906 if( (rStrm.GetNextRecId() == EXC_ID_CHTEXT) && rStrm.StartNextRecord() )
3907 {
3908 unique_ptr<XclImpChText> pText(new XclImpChText(GetChRoot()));
3909 pText->ReadRecordGroup(rStrm);
3910 m_DefTexts.insert(std::make_pair(nTextId, std::move(pText)));
3911 }
3912}
3913
3915{
3916 XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
3917 xDataFmt->ReadRecordGroup( rStrm );
3918 if( xDataFmt->GetPointPos().mnSeriesIdx <= EXC_CHSERIES_MAXSERIES )
3919 {
3920 const XclChDataPointPos& rPos = xDataFmt->GetPointPos();
3921 XclImpChDataFormatMap::iterator itr = maDataFmts.lower_bound(rPos);
3922 if (itr == maDataFmts.end() || maDataFmts.key_comp()(rPos, itr->first))
3923 // No element exists for this data point. Insert it.
3924 maDataFmts.insert(
3925 itr, XclImpChDataFormatMap::value_type(rPos, xDataFmt));
3926
3927 /* Do not overwrite existing data format group, Excel always uses the
3928 first data format group occurring in any CHSERIES group. */
3929 }
3930}
3931
3932void XclImpChChart::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
3933{
3934 if( !mxFrame )
3935 mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
3936 mxFrame->UpdateObjFrame( rLineData, rFillData );
3937}
3938
3940{
3941 XclImpChTypeGroupRef xTypeGroup = mxPrimAxesSet->GetTypeGroup( nGroupIdx );
3942 if( !xTypeGroup ) xTypeGroup = mxSecnAxesSet->GetTypeGroup( nGroupIdx );
3943 if( !xTypeGroup ) xTypeGroup = mxPrimAxesSet->GetFirstTypeGroup();
3944 return xTypeGroup;
3945}
3946
3948{
3949 sal_uInt16 nDefTextId = EXC_CHDEFTEXT_GLOBAL;
3950 bool bBiff8 = GetBiff() == EXC_BIFF8;
3951 switch( eTextType )
3952 {
3953 case EXC_CHTEXTTYPE_TITLE: nDefTextId = EXC_CHDEFTEXT_GLOBAL; break;
3954 case EXC_CHTEXTTYPE_LEGEND: nDefTextId = EXC_CHDEFTEXT_GLOBAL; break;
3955 case EXC_CHTEXTTYPE_AXISTITLE: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
3956 case EXC_CHTEXTTYPE_AXISLABEL: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
3957 case EXC_CHTEXTTYPE_DATALABEL: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
3958 }
3959
3960 XclImpChTextMap::const_iterator const itr = m_DefTexts.find(nDefTextId);
3961 return itr == m_DefTexts.end() ? nullptr : itr->second.get();
3962}
3963
3965{
3966 // there is no real automatic mode in BIFF5 charts
3968}
3969
3970void XclImpChChart::Convert( const Reference<XChartDocument>& xChartDoc,
3971 XclImpDffConverter& rDffConv, const OUString& rObjName, const tools::Rectangle& rChartRect ) const
3972{
3973 // initialize conversion (locks the model to suppress any internal updates)
3974 InitConversion( xChartDoc, rChartRect );
3975
3976 // chart frame formatting
3977 if( mxFrame )
3978 {
3979 ScfPropertySet aFrameProp( xChartDoc->getPageBackground() );
3980 mxFrame->Convert( aFrameProp );
3981 }
3982
3983 // chart title
3984 if( mxTitle ) try
3985 {
3986 Reference< XTitled > xTitled( xChartDoc, UNO_QUERY_THROW );
3987 Reference< XTitle > xTitle( mxTitle->CreateTitle(), UNO_SET_THROW );
3988 xTitled->setTitleObject( xTitle );
3989 }
3990 catch( Exception& )
3991 {
3992 }
3993
3994 /* Create the diagram object and attach it to the chart document. Currently,
3995 one diagram is used to carry all coordinate systems and data series. */
3996 Reference< XDiagram > xDiagram = CreateDiagram();
3997 xChartDoc->setFirstDiagram( xDiagram );
3998
3999 // coordinate systems and chart types, convert axis settings
4000 mxPrimAxesSet->Convert( xDiagram );
4001 mxSecnAxesSet->Convert( xDiagram );
4002
4003 // legend
4004 if( xDiagram.is() && mxLegend )
4005 xDiagram->setLegend( mxLegend->CreateLegend() );
4006
4007 /* Following all conversions needing the old Chart1 API that involves full
4008 initialization of the chart view. */
4009 Reference< cssc::XChartDocument > xChart1Doc( xChartDoc, UNO_QUERY );
4010 if( xChart1Doc.is() )
4011 {
4012 Reference< cssc::XDiagram > xDiagram1 = xChart1Doc->getDiagram();
4013
4014 /* Set the 'IncludeHiddenCells' property via the old API as only this
4015 ensures that the data provider and all created sequences get this
4016 flag correctly. */
4017 ScfPropertySet aDiaProp( xDiagram1 );
4018 bool bShowVisCells = ::get_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY );
4019 aDiaProp.SetBoolProperty( EXC_CHPROP_INCLUDEHIDDENCELLS, !bShowVisCells );
4020
4021 // plot area position and size (there is no real automatic mode in BIFF5 charts)
4022 XclImpChFramePosRef xPlotAreaPos = mxPrimAxesSet->GetPlotAreaFramePos();
4023 if( IsManualPlotArea() && xPlotAreaPos ) try
4024 {
4025 const XclChFramePos& rFramePos = xPlotAreaPos->GetFramePosData();
4026 if( (rFramePos.mnTLMode == EXC_CHFRAMEPOS_PARENT) && (rFramePos.mnBRMode == EXC_CHFRAMEPOS_PARENT) )
4027 {
4028 Reference< cssc::XDiagramPositioning > xPositioning( xDiagram1, UNO_QUERY_THROW );
4029 css::awt::Rectangle aDiagramRect = CalcHmmFromChartRect( rFramePos.maRect );
4030 // for pie charts, always set inner plot area size to exclude the data labels as Excel does
4031 const XclImpChTypeGroup* pFirstTypeGroup = mxPrimAxesSet->GetFirstTypeGroup().get();
4032 if( pFirstTypeGroup && (pFirstTypeGroup->GetTypeInfo().meTypeCateg == EXC_CHTYPECATEG_PIE) )
4033 xPositioning->setDiagramPositionExcludingAxes( aDiagramRect );
4034 else if( pFirstTypeGroup && pFirstTypeGroup->Is3dChart() )
4035 xPositioning->setDiagramPositionIncludingAxesAndAxisTitles( aDiagramRect );
4036 else
4037 xPositioning->setDiagramPositionIncludingAxes( aDiagramRect );
4038 }
4039 }
4040 catch( Exception& )
4041 {
4042 }
4043
4044 // positions of all title objects
4045 if( mxTitle )
4046 mxTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_TITLE ) );
4047 mxPrimAxesSet->ConvertTitlePositions();
4048 mxSecnAxesSet->ConvertTitlePositions();
4049 }
4050
4051 // unlock the model
4052 FinishConversion( rDffConv );
4053
4054 // start listening to this chart
4055 ScDocument& rDoc = GetRoot().GetDoc();
4056 ScChartListenerCollection* pChartCollection = rDoc.GetChartListenerCollection();
4057 if(!pChartCollection)
4058 return;
4059
4060 std::vector< ScTokenRef > aRefTokens;
4061 for( const auto& rxSeries : maSeries )
4062 rxSeries->FillAllSourceLinks( aRefTokens );
4063 if( !aRefTokens.empty() )
4064 {
4065 ::std::unique_ptr< ScChartListener > xListener( new ScChartListener( rObjName, rDoc, std::move(aRefTokens) ) );
4066 xListener->SetUsed( true );
4067 xListener->StartListeningTo();
4068 pChartCollection->insert( xListener.release() );
4069 }
4070}
4071
4073{
4074 sal_uInt16 nNewSeriesIdx = static_cast< sal_uInt16 >( maSeries.size() );
4075 XclImpChSeriesRef xSeries = std::make_shared<XclImpChSeries>( GetChRoot(), nNewSeriesIdx );
4076 xSeries->ReadRecordGroup( rStrm );
4077 maSeries.push_back( xSeries );
4078}
4079
4081{
4082 maProps.mnFlags = rStrm.ReaduInt16();
4083 maProps.mnEmptyMode = rStrm.ReaduInt8();
4084}
4085
4087{
4088 XclImpChAxesSetRef xAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_NONE );
4089 xAxesSet->ReadRecordGroup( rStrm );
4090 switch( xAxesSet->GetAxesSetId() )
4091 {
4092 case EXC_CHAXESSET_PRIMARY: mxPrimAxesSet = xAxesSet; break;
4093 case EXC_CHAXESSET_SECONDARY: mxSecnAxesSet = xAxesSet; break;
4094 }
4095}
4096
4098{
4099 XclImpChTextRef xText = std::make_shared<XclImpChText>( GetChRoot() );
4100 xText->ReadRecordGroup( rStrm );
4101 switch( xText->GetLinkTarget() )
4102 {
4104 mxTitle = xText;
4105 break;
4106 case EXC_CHOBJLINK_DATA:
4107 {
4108 sal_uInt16 nSeriesIdx = xText->GetPointPos().mnSeriesIdx;
4109 if( nSeriesIdx < maSeries.size() )
4110 maSeries[ nSeriesIdx ]->SetDataLabel( xText );
4111 }
4112 break;
4113 }
4114}
4115
4117{
4118 // finalize series (must be done first)
4120 // #i49218# legend may be attached to primary or secondary axes set
4121 mxLegend = mxPrimAxesSet->GetLegend();
4122 if( !mxLegend )
4123 mxLegend = mxSecnAxesSet->GetLegend();
4124 if( mxLegend )
4125 mxLegend->Finalize();
4126 // axes sets, updates chart type group default formats -> must be called before FinalizeDataFormats()
4127 mxPrimAxesSet->Finalize();
4128 mxSecnAxesSet->Finalize();
4129 // formatting of all series
4131 // #i47745# missing frame -> invisible border and area
4132 if( !mxFrame )
4133 mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
4134 // chart title
4135 FinalizeTitle();
4136}
4137
4139{
4140 for( const XclImpChSeriesRef& xSeries : maSeries )
4141 {
4142 if( xSeries->HasParentSeries() )
4143 {
4144 /* Process child series (trend lines and error bars). Data of
4145 child series will be set at the connected parent series. */
4146 if( xSeries->GetParentIdx() < maSeries.size() )
4147 maSeries[ xSeries->GetParentIdx() ]->AddChildSeries( *xSeries );
4148 }
4149 else
4150 {
4151 // insert the series into the related chart type group
4152 if( XclImpChTypeGroup* pTypeGroup = GetTypeGroup( xSeries->GetGroupIdx() ).get() )
4153 pTypeGroup->AddSeries( xSeries );
4154 }
4155 }
4156}
4157
4159{
4160 /* #i51639# (part 1): CHDATAFORMAT groups are part of CHSERIES groups.
4161 Each CHDATAFORMAT group specifies the series and data point it is
4162 assigned to. This makes it possible to have a data format that is
4163 related to another series, e.g. a CHDATAFORMAT group for series 2 is
4164 part of a CHSERIES group that describes series 1. Therefore the chart
4165 itself has collected all CHDATAFORMAT groups to be able to store data
4166 format groups for series that have not been imported at that time. This
4167 loop finally assigns these groups to the related series. */
4168 for( const auto& [rPos, rDataFmt] : maDataFmts )
4169 {
4170 sal_uInt16 nSeriesIdx = rPos.mnSeriesIdx;
4171 if( nSeriesIdx < maSeries.size() )
4172 maSeries[ nSeriesIdx ]->SetDataFormat( rDataFmt );
4173 }
4174
4175 /* #i51639# (part 2): Finalize data formats of all series. This adds for
4176 example missing CHDATAFORMAT groups for entire series that are needed
4177 for automatic colors of lines and areas. */
4178 for( auto& rxSeries : maSeries )
4179 rxSeries->FinalizeDataFormats();
4180}
4181
4183{
4184 // special handling for auto-generated title
4185 OUString aAutoTitle;
4186 if( !mxTitle || (!mxTitle->IsDeleted() && !mxTitle->HasString()) )
4187 {
4188 // automatic title from first series name (if there are no series on secondary axes set)
4189 if( !mxSecnAxesSet->IsValidAxesSet() )
4190 aAutoTitle = mxPrimAxesSet->GetSingleSeriesTitle();
4191 if( mxTitle || (!aAutoTitle.isEmpty()) )
4192 {
4193 if( !mxTitle )
4194 mxTitle = std::make_shared<XclImpChText>( GetChRoot() );
4195 if( aAutoTitle.isEmpty() )
4196 aAutoTitle = ScResId(STR_CHARTTITLE);
4197 }
4198 }
4199
4200 // will reset mxTitle, if it does not contain a string and no auto title exists
4201 lclFinalizeTitle( mxTitle, GetDefaultText( EXC_CHTEXTTYPE_TITLE ), aAutoTitle );
4202}
4203
4204Reference< XDiagram > XclImpChChart::CreateDiagram() const
4205{
4206 // create a diagram object
4207 Reference< XDiagram > xDiagram( ScfApiHelper::CreateInstance( SERVICE_CHART2_DIAGRAM ), UNO_QUERY );
4208
4209 // convert global chart settings
4210 ScfPropertySet aDiaProp( xDiagram );
4211
4212 // treatment of missing values
4213 using namespace cssc::MissingValueTreatment;
4214 sal_Int32 nMissingValues = LEAVE_GAP;
4215 switch( maProps.mnEmptyMode )
4216 {
4217 case EXC_CHPROPS_EMPTY_SKIP: nMissingValues = LEAVE_GAP; break;
4218 case EXC_CHPROPS_EMPTY_ZERO: nMissingValues = USE_ZERO; break;
4219 case EXC_CHPROPS_EMPTY_INTERPOLATE: nMissingValues = CONTINUE; break;
4220 }
4221 aDiaProp.SetProperty( EXC_CHPROP_MISSINGVALUETREATMENT, nMissingValues );
4222
4223 return xDiagram;
4224}
4225
4227 XclImpDrawing( rRoot, bOwnTab ), // sheet charts may contain OLE objects
4228 mnScTab( rRoot.GetCurrScTab() ),
4229 mbOwnTab( bOwnTab )
4230{
4231}
4232
4234 const Reference< XModel >& rxModel, const tools::Rectangle& rChartRect )
4235{
4236 maChartRect = rChartRect; // needed in CalcAnchorRect() callback
4237
4238 SdrModel* pSdrModel = nullptr;
4239 SdrPage* pSdrPage = nullptr;
4240 if( mbOwnTab )
4241 {
4242 // chart sheet: insert all shapes into the sheet, not into the chart object
4243 pSdrModel = GetDoc().GetDrawLayer();
4244 pSdrPage = GetSdrPage( mnScTab );
4245 }
4246 else
4247 {
4248 // embedded chart object: insert all shapes into the chart
4249 try
4250 {
4251 Reference< XDrawPageSupplier > xDrawPageSupp( rxModel, UNO_QUERY_THROW );
4252 Reference< XDrawPage > xDrawPage( xDrawPageSupp->getDrawPage(), UNO_SET_THROW );
4253 pSdrPage = ::GetSdrPageFromXDrawPage( xDrawPage );
4254 pSdrModel = pSdrPage ? &pSdrPage->getSdrModelFromSdrPage() : nullptr;
4255 }
4256 catch( Exception& )
4257 {
4258 }
4259 }
4260
4261 if( pSdrModel && pSdrPage )
4262 ImplConvertObjects( rDffConv, *pSdrModel, *pSdrPage );
4263}
4264
4266{
4267 /* In objects with DFF client anchor, the position of the shape is stored
4268 in the cell address components of the client anchor. In old BIFF3-BIFF5
4269 objects, the position is stored in the offset components of the anchor. */
4270 tools::Rectangle aRect(
4271 static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maFirst.mnCol : rAnchor.mnLX ) / EXC_CHART_TOTALUNITS * maChartRect.GetWidth() + 0.5 ),
4272 static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maFirst.mnRow : rAnchor.mnTY ) / EXC_CHART_TOTALUNITS * maChartRect.GetHeight() + 0.5 ),
4273 static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maLast.mnCol : rAnchor.mnRX ) / EXC_CHART_TOTALUNITS * maChartRect.GetWidth() + 0.5 ),
4274 static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maLast.mnRow : rAnchor.mnBY ) / EXC_CHART_TOTALUNITS * maChartRect.GetHeight() + 0.5 ) );
4275 aRect.Normalize();
4276 // move shapes into chart area for sheet charts
4277 if( mbOwnTab )
4278 aRect.Move( maChartRect.Left(), maChartRect.Top() );
4279 return aRect;
4280}
4281
4283{
4284}
4285
4286XclImpChart::XclImpChart( const XclImpRoot& rRoot, bool bOwnTab ) :
4287 XclImpRoot( rRoot ),
4288 mbOwnTab( bOwnTab ),
4289 mbIsPivotChart( false )
4290{
4291}
4292
4294{
4295}
4296
4298{
4299 XclImpPageSettings& rPageSett = GetPageSettings();
4300 XclImpTabViewSettings& rTabViewSett = GetTabViewSettings();
4301
4302 bool bLoop = true;
4303 while( bLoop && rStrm.StartNextRecord() )
4304 {
4305 // page settings - only for charts in entire sheet
4306 if( mbOwnTab ) switch( rStrm.GetRecId() )
4307 {
4309 case EXC_ID_VERPAGEBREAKS: rPageSett.ReadPageBreaks( rStrm ); break;
4310 case EXC_ID_HEADER:
4311 case EXC_ID_FOOTER: rPageSett.ReadHeaderFooter( rStrm ); break;
4312 case EXC_ID_LEFTMARGIN:
4313 case EXC_ID_RIGHTMARGIN:
4314 case EXC_ID_TOPMARGIN:
4315 case EXC_ID_BOTTOMMARGIN: rPageSett.ReadMargin( rStrm ); break;
4316 case EXC_ID_PRINTHEADERS: rPageSett.ReadPrintHeaders( rStrm ); break;
4317 case EXC_ID_PRINTGRIDLINES: rPageSett.ReadPrintGridLines( rStrm ); break;
4318 case EXC_ID_HCENTER:
4319 case EXC_ID_VCENTER: rPageSett.ReadCenter( rStrm ); break;
4320 case EXC_ID_SETUP: rPageSett.ReadSetup( rStrm ); break;
4321 case EXC_ID8_IMGDATA: rPageSett.ReadImgData( rStrm ); break;
4322
4323 case EXC_ID_WINDOW2: rTabViewSett.ReadWindow2( rStrm, true );break;
4324 case EXC_ID_SCL: rTabViewSett.ReadScl( rStrm ); break;
4325
4326 case EXC_ID_SHEETEXT: //0x0862
4327 {
4328 // FIXME: do not need to pass palette, XclImpTabVieSettings is derived from root
4329 XclImpPalette& rPal = GetPalette();
4330 rTabViewSett.ReadTabBgColor( rStrm, rPal);
4331 }
4332 break;
4333
4334 case EXC_ID_CODENAME: ReadCodeName( rStrm, false ); break;
4335 }
4336
4337 // common records
4338 switch( rStrm.GetRecId() )
4339 {
4340 case EXC_ID_EOF: bLoop = false; break;
4341
4342 // #i31882# ignore embedded chart objects
4343 case EXC_ID2_BOF:
4344 case EXC_ID3_BOF:
4345 case EXC_ID4_BOF:
4347
4348 case EXC_ID_CHCHART: ReadChChart( rStrm ); break;
4349
4350 case EXC_ID8_CHPIVOTREF:
4352 mbIsPivotChart = true;
4353 break;
4354
4355 // BIFF specific records
4356 default: switch( GetBiff() )
4357 {
4358 case EXC_BIFF5: switch( rStrm.GetRecId() )
4359 {
4360 case EXC_ID_OBJ: GetChartDrawing().ReadObj( rStrm ); break;
4361 }
4362 break;
4363 case EXC_BIFF8: switch( rStrm.GetRecId() )
4364 {
4366 // #i61786# weird documents: OBJ without MSODRAWING -> read in BIFF5 format
4367 case EXC_ID_OBJ: GetChartDrawing().ReadObj( rStrm ); break;
4368 }
4369 break;
4370 default:;
4371 }
4372 }
4373 }
4374}
4375
4376void XclImpChart::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
4377{
4378 if( !mxChartData )
4379 mxChartData = std::make_shared<XclImpChChart>( GetRoot() );
4380 mxChartData->UpdateObjFrame( rLineData, rFillData );
4381}
4382
4384{
4385 return
4387 (mxChartDrawing ? mxChartDrawing->GetProgressSize() : 0);
4388}
4389
4390void XclImpChart::Convert( Reference< XModel > const & xModel, XclImpDffConverter& rDffConv, const OUString& rObjName, const tools::Rectangle& rChartRect ) const
4391{
4392 Reference< XChartDocument > xChartDoc( xModel, UNO_QUERY );
4393 if( xChartDoc.is() )
4394 {
4395 if( mxChartData )
4396 mxChartData->Convert( xChartDoc, rDffConv, rObjName, rChartRect );
4397 if( mxChartDrawing )
4398 mxChartDrawing->ConvertObjects( rDffConv, xModel, rChartRect );
4399 }
4400}
4401
4403{
4404 if( !mxChartDrawing )
4405 mxChartDrawing = std::make_shared<XclImpChartDrawing>( GetRoot(), mbOwnTab );
4406 return *mxChartDrawing;
4407}
4408
4410{
4411 mxChartData = std::make_shared<XclImpChChart>( GetRoot() );
4412 mxChartData->ReadRecordGroup( rStrm );
4413}
4414
4415/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SCTAB Tab() const
Definition: address.hxx:283
SCROW Row() const
Definition: address.hxx:274
SCCOL Col() const
Definition: address.hxx:279
bool insert(ScChartListener *pListener)
Definition: chartlis.cxx:388
SC_DLLPUBLIC ScChartListenerCollection * GetChartListenerCollection() const
Definition: document.hxx:2228
SC_DLLPUBLIC ScDrawLayer * GetDrawLayer()
Definition: document.hxx:1082
static css::uno::Sequence< Type > VectorToSequence(const ::std::vector< Type > &rVector)
Converts a non-empty vector into a UNO sequence containing elements of the same type.
Definition: fapihelper.hxx:80
static css::uno::Reference< css::uno::XInterface > CreateInstance(const css::uno::Reference< css::lang::XMultiServiceFactory > &xFactory, const OUString &rServiceName)
Creates an instance from the passed service name, using the passed service factory.
A wrapper for a UNO property set.
Definition: fapihelper.hxx:104
void SetProperty(const OUString &rPropName, const Type &rValue)
Puts the passed value into the property set.
Definition: fapihelper.hxx:174
void Set(css::uno::Reference< css::beans::XPropertySet > const &xPropSet)
Sets the passed UNO property set and releases the old UNO property set.
void SetColorProperty(const OUString &rPropName, const Color &rColor)
Puts the passed color into the property set.
Definition: fapihelper.hxx:186
void SetStringProperty(const OUString &rPropName, const OUString &rValue)
Puts the passed string into the property set.
Definition: fapihelper.hxx:182
void SetBoolProperty(const OUString &rPropName, bool bValue)
Puts the passed Boolean value into the property set.
Definition: fapihelper.hxx:178
bool GetProperty(Type &rValue, const OUString &rPropName) const
Gets the specified property from the property set.
Definition: fapihelper.hxx:148
void SetAnyProperty(const OUString &rPropName, const css::uno::Any &rValue)
Puts the passed Any into the property set.
Definition: fapihelper.cxx:233
static Color GetMixedColor(const Color &rFore, const Color &rBack, sal_uInt8 nTrans)
Mixes colors with given transparence.
Definition: ftools.cxx:137
const SfxItemPool & GetItemPool() const
SdrModel & getSdrModelFromSdrPage() const
css::uno::Reference< css::frame::XModel3 > GetModel() const
SvStream & ReadDouble(double &rDouble)
SvStream & ReadInt16(sal_Int16 &rInt16)
SvStream & ReadInt32(sal_Int32 &rInt32)
static void WriteMarkerProperties(ScfPropertySet &rPropSet, const XclChMarkerFormat &rMarkerFmt)
Writes all marker properties to the passed property set.
Definition: xlchart.cxx:1062
static void WriteRotationProperties(ScfPropertySet &rPropSet, sal_uInt16 nRotation, bool bSupportsStacked)
Writes rotation properties to the passed property set.
Definition: xlchart.cxx:1096
void WriteAreaProperties(ScfPropertySet &rPropSet, const XclChAreaFormat &rAreaFmt, XclChPropertyMode ePropMode)
Writes solid area properties to the passed property set.
Definition: xlchart.cxx:973
void WriteEscherProperties(ScfPropertySet &rPropSet, XclChObjectTable &rGradientTable, XclChObjectTable &rBitmapTable, const XclChEscherFormat &rEscherFmt, const XclChPicFormat *pPicFmt, sal_uInt32 nDffFillType, XclChPropertyMode ePropMode)
Writes gradient or bitmap area properties to the passed property set.
Definition: xlchart.cxx:993
void WriteLineProperties(ScfPropertySet &rPropSet, XclChObjectTable &rDashTable, const XclChLineFormat &rLineFmt, XclChPropertyMode ePropMode)
Writes all line properties to the passed property set.
Definition: xlchart.cxx:907
static sal_uInt16 GetSeriesFillAutoColorIdx(sal_uInt16 nFormatIdx)
Returns a palette index for automatic series fill colors.
Definition: xlchart.cxx:366
static sal_uInt16 GetSeriesLineAutoColorIdx(sal_uInt16 nFormatIdx)
Returns a palette index for automatic series line colors.
Definition: xlchart.cxx:351
static sal_uInt8 GetSeriesFillAutoTransp(sal_uInt16 nFormatIdx)
Returns a transparency value for automatic series fill colors.
Definition: xlchart.cxx:381
static sal_uInt16 GetAutoMarkerType(sal_uInt16 nFormatIdx)
Returns an automatic symbol index for the passed format index.
Definition: xlchart.cxx:387
static OUString GetErrorBarValuesRole(sal_uInt8 nBarType)
Returns the role name for a manual data source for error bars.
Definition: xlchart.cxx:403
void Convert(ScfPropertySet &rPropSet) const
Converts and writes the contained data to the passed property set.
Definition: xichart.cxx:1355
void ReadCh3dDataFormat(XclImpStream &rStrm)
Reads the CH3DDATAFORMAT record (3D bar properties).
Definition: xichart.cxx:1349
XclCh3dDataFormat maData
Definition: xichart.hxx:628
XclChAreaFormat maData
Definition: xichart.hxx:287
void ReadChAreaFormat(XclImpStream &rStrm)
Reads the CHAREAFORMAT record (basic fill properties, e.g.
Definition: xichart.cxx:486
bool IsAuto() const
Returns true, if the area format is set to automatic.
Definition: xichart.hxx:277
void Convert(const XclImpChRoot &rRoot, ScfPropertySet &rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx) const
Converts and writes the contained data to the passed property set.
Definition: xichart.cxx:502
sal_uInt16 mnFlags
Definition: xichart.hxx:644
void ReadChAttachedLabel(XclImpStream &rStrm)
Reads the CHATTACHEDLABEL record (data series/point labels).
Definition: xichart.cxx:1370
XclImpChAttachedLabel(const XclImpChRoot &rRoot)
Definition: xichart.cxx:1364
XclImpChTextRef CreateDataLabel(const XclImpChText *pParent) const
Creates a CHTEXT group for the label.
Definition: xichart.cxx:1375
void ConvertBackground(css::uno::Reference< css::chart2::XDiagram > const &xDiagram) const
Writes all properties of the background area to the passed diagram.
Definition: xichart.cxx:3830
XclImpChTextRef mxXAxisTitle
The Z axis (CHAXIS group).
Definition: xichart.hxx:1276
::std::map< sal_uInt16, XclImpChTypeGroupRef > XclImpChTypeGroupMap
Definition: xichart.hxx:1269
XclChAxesSet maData
Definition: xichart.hxx:1271
void Convert(css::uno::Reference< css::chart2::XDiagram > const &xDiagram) const
Creates a coordinate system and converts all series and axis settings.
Definition: xichart.cxx:3640
XclImpChAxesSet(const XclImpChRoot &rRoot, sal_uInt16 nAxesSetId)
Definition: xichart.cxx:3529
sal_Int32 GetApiAxesSetIndex() const
Returns the axes set index used by the chart API.
Definition: xichart.hxx:1227
OUString GetSingleSeriesTitle() const
Returns series title, if the axes set contains only one single series.
Definition: xichart.cxx:3635
void ConvertTitlePositions() const
Converts the manual positions of all axis titles.
Definition: xichart.cxx:3673
css::uno::Reference< css::chart2::XCoordinateSystem > CreateCoordSystem(css::uno::Reference< css::chart2::XDiagram > const &xDiagram) const
Creates a coordinate system that contains all chart types for this axes set.
Definition: xichart.cxx:3732
bool IsValidAxesSet() const
Returns true, if this axes set exists (returns false if this is a dummy object).
Definition: xichart.hxx:1223
void Finalize()
Final processing after reading the entire chart.
Definition: xichart.cxx:3564
XclImpChTextRef mxYAxisTitle
The X axis title (CHTEXT group).
Definition: xichart.hxx:1277
XclImpChAxisRef mxXAxis
Outer plot area position (CHFRAMEPOS record).
Definition: xichart.hxx:1273
XclImpChAxisRef mxZAxis
The Y axis (CHAXIS group).
Definition: xichart.hxx:1275
XclImpChAxisRef mxYAxis
The X axis (CHAXIS group).
Definition: xichart.hxx:1274
virtual void ReadSubRecord(XclImpStream &rStrm) override
Reads a record from the CHAXESSET group (called by base class).
Definition: xichart.cxx:3541
void ReadChPlotFrame(XclImpStream &rStrm)
Reads the CHPLOTFRAME record group containing diagram area formatting.
Definition: xichart.cxx:3709
void ReadChAxis(XclImpStream &rStrm)
Reads a CHAXIS record group containing a single axis.
Definition: xichart.cxx:3683
void ReadChText(XclImpStream &rStrm)
Reads a CHTEXT record group containing an axis title.
Definition: xichart.cxx:3696
XclImpChLegendRef GetLegend() const
Looks for a legend in all chart type groups and returns it.
Definition: xichart.cxx:3623
css::uno::Reference< css::chart2::XAxis > CreateAxis(const XclImpChAxis &rChAxis, const XclImpChAxis *pCrossingAxis) const
Creates and returns an API axis object.
Definition: xichart.cxx:3822
void ConvertAxis(XclImpChAxisRef const &xChAxis, XclImpChTextRef const &xChAxisTitle, css::uno::Reference< css::chart2::XCoordinateSystem > const &xCoordSystem, const XclImpChAxis *pCrossingAxis) const
Creates and inserts an axis into the container and registers the coordinate system.
Definition: xichart.cxx:3785
sal_uInt16 GetAxesSetId() const
Returns the index