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();
952
953 Color aColor;
954 rStrm >> aColor;
956
958 maData.mnFlags = rStrm.ReaduInt16();
959
960 if( GetBiff() == EXC_BIFF8 )
961 {
962 // BIFF8: index into palette used instead of RGB data
963 aColor = GetPalette().GetColor(rStrm.ReaduInt16());
965 // placement and rotation
966 maData.mnFlags2 = rStrm.ReaduInt16();
967 maData.mnRotation = rStrm.ReaduInt16();
968 }
969 else
970 {
971 // BIFF2-BIFF7: get rotation from text orientation
972 sal_uInt8 nOrient = ::extract_value< sal_uInt8 >( maData.mnFlags, 8, 3 );
974 }
975}
976
978{
979 switch( rStrm.GetRecId() )
980 {
982 mxFramePos = std::make_shared<XclImpChFramePos>();
983 mxFramePos->ReadChFramePos( rStrm );
984 break;
985 case EXC_ID_CHFONT:
986 mxFont = std::make_shared<XclImpChFont>();
987 mxFont->ReadChFont( rStrm );
988 break;
990 if( GetBiff() == EXC_BIFF8 )
992 break;
994 mxSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
995 mxSrcLink->ReadChSourceLink( rStrm );
996 break;
997 case EXC_ID_CHFRAME:
998 mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_TEXT );
999 mxFrame->ReadRecordGroup( rStrm );
1000 break;
1002 maObjLink.mnTarget = rStrm.ReaduInt16();
1003 maObjLink.maPointPos.mnSeriesIdx = rStrm.ReaduInt16();
1004 maObjLink.maPointPos.mnPointIdx = rStrm.ReaduInt16();
1005 break;
1008 break;
1009 case EXC_ID_CHEND:
1010 if( mxSrcLink && !maFormats.empty() )
1011 mxSrcLink->SetTextFormats( std::vector(maFormats) );
1012 break;
1013 }
1014}
1015
1017{
1018 return mxFont ? mxFont->GetFontIndex() : EXC_FONT_NOTFOUND;
1019}
1020
1022{
1024}
1025
1027{
1028 return maData.mnRotation;
1029}
1030
1031void XclImpChText::SetString( const OUString& rString )
1032{
1033 if( !mxSrcLink )
1034 mxSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
1035 mxSrcLink->SetString( rString );
1036}
1037
1038void XclImpChText::UpdateText( const XclImpChText* pParentText )
1039{
1040 if( !pParentText )
1041 return;
1042
1043 // update missing members
1044 if( !mxFrame )
1045 mxFrame = pParentText->mxFrame;
1046 if( !mxFont )
1047 {
1048 mxFont = pParentText->mxFont;
1049 // text color is taken from CHTEXT record, not from font in CHFONT
1052 }
1053}
1054
1055void XclImpChText::UpdateDataLabel( bool bCateg, bool bValue, bool bPercent )
1056{
1060 ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC, bCateg && bPercent );
1061 ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED, !bCateg && !bValue && !bPercent );
1062}
1063
1065{
1066 ConvertFontBase( GetChRoot(), rPropSet );
1067}
1068
1069void XclImpChText::ConvertRotation( ScfPropertySet& rPropSet, bool bSupportsStacked ) const
1070{
1071 ConvertRotationBase( rPropSet, bSupportsStacked );
1072}
1073
1075{
1076 if( mxFrame )
1077 mxFrame->Convert( rPropSet );
1078}
1079
1080void XclImpChText::ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const
1081{
1082 if( mxSrcLink )
1083 mxSrcLink->ConvertNumFmt( rPropSet, bPercent );
1084}
1085
1086void XclImpChText::ConvertDataLabel( ScfPropertySet& rPropSet, const XclChTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet ) const
1087{
1088 // existing CHFRLABELPROPS record wins over flags from CHTEXT
1089 sal_uInt16 nShowFlags = mxLabelProps ? mxLabelProps->mnFlags : maData.mnFlags;
1094
1095 // get raw flags for label values
1096 bool bShowNone = IsDeleted();
1097 bool bShowCateg = !bShowNone && ::get_flag( nShowFlags, SHOWANYCATEG );
1098 bool bShowPercent = !bShowNone && ::get_flag( nShowFlags, SHOWANYPERCENT );
1099 bool bShowValue = !bShowNone && ::get_flag( nShowFlags, SHOWANYVALUE );
1100 bool bShowBubble = !bShowNone && ::get_flag( nShowFlags, SHOWANYBUBBLE );
1101
1102 // adjust to Chart2 behaviour
1103 if( rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES )
1104 bShowValue = bShowBubble; // Chart2 bubble charts show bubble size if 'ShowValue' is set
1105
1106 // other flags
1107 bool bShowAny = bShowValue || bShowPercent || bShowCateg;
1108 bool bShowSymbol = bShowAny && ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWSYMBOL );
1109
1110 // create API struct for label values, set API label separator
1111 cssc2::DataPointLabel aPointLabel( bShowValue, bShowPercent, bShowCateg, bShowSymbol, false, false );
1112 rPropSet.SetProperty( EXC_CHPROP_LABEL, aPointLabel );
1113 OUString aSep = mxLabelProps ? mxLabelProps->maSeparator : OUString('\n');
1114 if( aSep.isEmpty() )
1115 aSep = "; ";
1117
1118 // text properties of attached label
1119 if( !bShowAny )
1120 return;
1121
1122 ConvertFont( rPropSet );
1123 ConvertRotation( rPropSet, false );
1124 // label placement
1125 using namespace cssc::DataLabelPlacement;
1126 sal_Int32 nPlacement = rTypeInfo.mnDefaultLabelPos;
1127 switch( ::extract_value< sal_uInt16 >( maData.mnFlags2, 0, 4 ) )
1128 {
1129 case EXC_CHTEXT_POS_DEFAULT: nPlacement = rTypeInfo.mnDefaultLabelPos; break;
1130 case EXC_CHTEXT_POS_OUTSIDE: nPlacement = OUTSIDE; break;
1131 case EXC_CHTEXT_POS_INSIDE: nPlacement = INSIDE; break;
1132 case EXC_CHTEXT_POS_CENTER: nPlacement = CENTER; break;
1133 case EXC_CHTEXT_POS_AXIS: nPlacement = NEAR_ORIGIN; break;
1134 case EXC_CHTEXT_POS_ABOVE: nPlacement = TOP; break;
1135 case EXC_CHTEXT_POS_BELOW: nPlacement = BOTTOM; break;
1136 case EXC_CHTEXT_POS_LEFT: nPlacement = LEFT; break;
1137 case EXC_CHTEXT_POS_RIGHT: nPlacement = RIGHT; break;
1138 case EXC_CHTEXT_POS_AUTO: nPlacement = AVOID_OVERLAP; break;
1139 }
1140 sal_Int32 nGlobalPlacement = 0;
1141 if ( ( nPlacement == rTypeInfo.mnDefaultLabelPos ) && pGlobalPropSet &&
1142 pGlobalPropSet->GetProperty( nGlobalPlacement, EXC_CHPROP_LABELPLACEMENT ) )
1143 nPlacement = nGlobalPlacement;
1144
1145 rPropSet.SetProperty( EXC_CHPROP_LABELPLACEMENT, nPlacement );
1146 // label number format (percentage format wins over value format)
1147 if( bShowPercent || bShowValue )
1148 ConvertNumFmt( rPropSet, bShowPercent );
1149}
1150
1151Reference< XTitle > XclImpChText::CreateTitle() const
1152{
1153 Reference< XTitle > xTitle;
1154 if( mxSrcLink && mxSrcLink->HasString() )
1155 {
1156 // create the formatted strings
1157 Sequence< Reference< XFormattedString > > aStringSeq(
1158 mxSrcLink->CreateStringSequence( GetChRoot(), GetFontIndex(), GetFontColor() ) );
1159 if( aStringSeq.hasElements() )
1160 {
1161 // create the title object
1162 xTitle.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_TITLE ), UNO_QUERY );
1163 if( xTitle.is() )
1164 {
1165 // set the formatted strings
1166 xTitle->setText( aStringSeq );
1167 // more title formatting properties
1168 ScfPropertySet aTitleProp( xTitle );
1169 ConvertFrame( aTitleProp );
1170 ConvertRotation( aTitleProp, true );
1171 }
1172 }
1173 }
1174 return xTitle;
1175}
1176
1178{
1179 if( !mxFramePos ) return;
1180
1181 const XclChFramePos& rPosData = mxFramePos->GetFramePosData();
1182 OSL_ENSURE( (rPosData.mnTLMode == EXC_CHFRAMEPOS_PARENT) && (rPosData.mnBRMode == EXC_CHFRAMEPOS_PARENT),
1183 "XclImpChText::ConvertTitlePosition - unexpected frame position mode" );
1184
1185 /* Check if title is moved manually. To get the actual position of the
1186 title, we do some kind of hack and use the values from the CHTEXT
1187 record, effectively ignoring the contents of the CHFRAMEPOS record
1188 which contains the position relative to the default title position
1189 (according to the spec, the CHFRAMEPOS supersedes the CHTEXT record).
1190 Especially when it comes to axis titles, things would become very
1191 complicated here, because the relative title position is stored in a
1192 measurement unit that is dependent on the size of the inner plot area,
1193 the interpretation of the X and Y coordinate is dependent on the
1194 direction of the axis, and in 3D charts, and the title default
1195 positions are dependent on the 3D view settings (rotation, elevation,
1196 and perspective). Thus, it is easier to assume that the creator has
1197 written out the correct absolute position and size of the title in the
1198 CHTEXT record. This is assured by checking that the shape size stored
1199 in the CHTEXT record is non-zero. */
1200 if( !((rPosData.mnTLMode == EXC_CHFRAMEPOS_PARENT) &&
1201 ((rPosData.maRect.mnX != 0) || (rPosData.maRect.mnY != 0)) &&
1202 (maData.maRect.mnWidth > 0) && (maData.maRect.mnHeight > 0)) )
1203 return;
1204
1205 try
1206 {
1207 Reference< XShape > xTitleShape( GetTitleShape( rTitleKey ), UNO_SET_THROW );
1208 // the call to XShape.getSize() may recalc the chart view
1209 css::awt::Size aTitleSize = xTitleShape->getSize();
1210 // rotated titles need special handling...
1211 Degree100 nScRot = XclTools::GetScRotation( GetRotation(), 0_deg100 );
1212 double fRad = toRadians(nScRot);
1213 double fSin = fabs( sin( fRad ) );
1214 // calculate the title position from the values in the CHTEXT record
1215 css::awt::Point aTitlePos(
1218 // add part of height to X direction, if title is rotated down (clockwise)
1219 if( nScRot > 18000_deg100 )
1220 aTitlePos.X += static_cast< sal_Int32 >( fSin * aTitleSize.Height + 0.5 );
1221 // add part of width to Y direction, if title is rotated up (counterclockwise)
1222 else if( nScRot > 0_deg100 )
1223 aTitlePos.Y += static_cast< sal_Int32 >( fSin * aTitleSize.Width + 0.5 );
1224 // set the resulting position at the title shape
1225 xTitleShape->setPosition( aTitlePos );
1226 }
1227 catch( Exception& )
1228 {
1229 }
1230}
1231
1233{
1234 if( GetBiff() == EXC_BIFF8 )
1235 {
1236 mxLabelProps = std::make_shared<XclChFrLabelProps>();
1237 sal_uInt16 nSepLen;
1238 rStrm.Ignore( 12 );
1239 mxLabelProps->mnFlags = rStrm.ReaduInt16();
1240 nSepLen = rStrm.ReaduInt16();
1241 if( nSepLen > 0 )
1242 mxLabelProps->maSeparator = rStrm.ReadUniString( nSepLen );
1243 }
1244}
1245
1246namespace {
1247
1248void lclUpdateText( XclImpChTextRef& rxText, const XclImpChText* xDefText )
1249{
1250 if (rxText)
1251 rxText->UpdateText( xDefText );
1252 else if (xDefText)
1253 {
1254 rxText = std::make_shared<XclImpChText>(*xDefText);
1255 }
1256}
1257
1258void lclFinalizeTitle( XclImpChTextRef& rxTitle, const XclImpChText* pDefText, const OUString& rAutoTitle )
1259{
1260 /* Do not update a title, if it is not visible (if rxTitle is null).
1261 Existing reference indicates enabled title. */
1262 if( rxTitle )
1263 {
1264 if( !rxTitle->HasString() )
1265 rxTitle->SetString( rAutoTitle );
1266 if( rxTitle->HasString() )
1267 rxTitle->UpdateText(pDefText);
1268 else
1269 rxTitle.reset();
1270 }
1271}
1272
1273} // namespace
1274
1275// Data series ================================================================
1276
1278{
1280 maData.mnMarkerType = rStrm.ReaduInt16();
1281 maData.mnFlags = rStrm.ReaduInt16();
1282
1283 const XclImpRoot& rRoot = rStrm.GetRoot();
1284 if( rRoot.GetBiff() == EXC_BIFF8 )
1285 {
1286 // BIFF8: index into palette used instead of RGB data
1287 const XclImpPalette& rPal = rRoot.GetPalette();
1288 maData.maLineColor = rPal.GetColor( rStrm.ReaduInt16() );
1289 maData.maFillColor = rPal.GetColor( rStrm.ReaduInt16() );
1290 // marker size
1291 maData.mnMarkerSize = rStrm.ReaduInt32();
1292 }
1293}
1294
1296 ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx, sal_Int16 nLineWeight ) const
1297{
1298 if( IsAuto() )
1299 {
1300 XclChMarkerFormat aMarkerFmt;
1301 // line and fill color of the symbol are equal to series line color
1302 //TODO: Excel sets no fill color for specific symbols (e.g. cross)
1303 aMarkerFmt.maLineColor = aMarkerFmt.maFillColor = rRoot.GetSeriesLineAutoColor( nFormatIdx );
1304 switch( nLineWeight )
1305 {
1310 default: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_SINGLESIZE;
1311 }
1312 aMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx );
1313 XclChPropSetHelper::WriteMarkerProperties( rPropSet, aMarkerFmt );
1314 }
1315 else
1316 {
1318 }
1319}
1320
1322 ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const
1323{
1324 Color aLineColor = IsAuto() ? rRoot.GetSeriesLineAutoColor( nFormatIdx ) : maData.maFillColor;
1325 rPropSet.SetColorProperty( EXC_CHPROP_COLOR, aLineColor );
1326}
1327
1329 mnPieDist( 0 )
1330{
1331}
1332
1334{
1335 mnPieDist = rStrm.ReaduInt16();
1336}
1337
1339{
1340 double fApiDist = ::std::min< double >( mnPieDist / 100.0, 1.0 );
1341 rPropSet.SetProperty( EXC_CHPROP_OFFSET, fApiDist );
1342}
1343
1345 mnFlags( 0 )
1346{
1347}
1348
1350{
1351 mnFlags = rStrm.ReaduInt16();
1352}
1353
1355{
1356 maData.mnBase = rStrm.ReaduInt8();
1357 maData.mnTop = rStrm.ReaduInt8();
1358}
1359
1361{
1362 using namespace ::com::sun::star::chart2::DataPointGeometry3D;
1363 sal_Int32 nApiType = (maData.mnBase == EXC_CH3DDATAFORMAT_RECT) ?
1364 ((maData.mnTop == EXC_CH3DDATAFORMAT_STRAIGHT) ? CUBOID : PYRAMID) :
1365 ((maData.mnTop == EXC_CH3DDATAFORMAT_STRAIGHT) ? CYLINDER : CONE);
1366 rPropSet.SetProperty( EXC_CHPROP_GEOMETRY3D, nApiType );
1367}
1368
1370 XclImpChRoot( rRoot ),
1371 mnFlags( 0 )
1372{
1373}
1374
1376{
1377 mnFlags = rStrm.ReaduInt16();
1378}
1379
1381{
1382 const sal_uInt16 EXC_CHATTLABEL_SHOWANYVALUE = EXC_CHATTLABEL_SHOWVALUE;
1383 const sal_uInt16 EXC_CHATTLABEL_SHOWANYPERCENT = EXC_CHATTLABEL_SHOWPERCENT | EXC_CHATTLABEL_SHOWCATEGPERC;
1384 const sal_uInt16 EXC_CHATTLABEL_SHOWANYCATEG = EXC_CHATTLABEL_SHOWCATEG | EXC_CHATTLABEL_SHOWCATEGPERC;
1385
1386 XclImpChTextRef xLabel;
1387 if ( pParent )
1388 xLabel = std::make_shared<XclImpChText>( *pParent );
1389 else
1390 xLabel = std::make_shared<XclImpChText>( GetChRoot() );
1391 xLabel->UpdateDataLabel(
1392 ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYCATEG ),
1393 ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYVALUE ),
1394 ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYPERCENT ) );
1395 return xLabel;
1396}
1397
1399 XclImpChRoot( rRoot )
1400{
1401}
1402
1404{
1405 maData.maPointPos.mnPointIdx = rStrm.ReaduInt16();
1406 maData.maPointPos.mnSeriesIdx = rStrm.ReaduInt16();
1407 maData.mnFormatIdx = rStrm.ReaduInt16();
1408 maData.mnFlags = rStrm.ReaduInt16();
1409}
1410
1412{
1413 switch( rStrm.GetRecId() )
1414 {
1416 mxMarkerFmt = std::make_shared<XclImpChMarkerFormat>();
1417 mxMarkerFmt->ReadChMarkerFormat( rStrm );
1418 break;
1419 case EXC_ID_CHPIEFORMAT:
1420 mxPieFmt = std::make_shared<XclImpChPieFormat>();
1421 mxPieFmt->ReadChPieFormat( rStrm );
1422 break;
1424 mxSeriesFmt = std::make_shared<XclImpChSeriesFormat>();
1425 mxSeriesFmt->ReadChSeriesFormat( rStrm );
1426 break;
1428 mx3dDataFmt = std::make_shared<XclImpCh3dDataFormat>();
1429 mx3dDataFmt->ReadCh3dDataFormat( rStrm );
1430 break;
1432 mxAttLabel = std::make_shared<XclImpChAttachedLabel>( GetChRoot() );
1433 mxAttLabel->ReadChAttachedLabel( rStrm );
1434 break;
1435 default:
1437 }
1438}
1439
1440void XclImpChDataFormat::SetPointPos( const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx )
1441{
1442 maData.maPointPos = rPointPos;
1443 maData.mnFormatIdx = nFormatIdx;
1444}
1445
1447{
1448 // remove formats not used for the current chart type
1449 RemoveUnusedFormats( rTypeInfo );
1450}
1451
1453{
1454 // update missing formats from passed chart type group format
1455 if( pGroupFmt )
1456 {
1457 if( !mxLineFmt )
1458 mxLineFmt = pGroupFmt->mxLineFmt;
1459 if( !mxAreaFmt && !mxEscherFmt )
1460 {
1461 mxAreaFmt = pGroupFmt->mxAreaFmt;
1462 mxEscherFmt = pGroupFmt->mxEscherFmt;
1463 }
1464 if( !mxMarkerFmt )
1465 mxMarkerFmt = pGroupFmt->mxMarkerFmt;
1466 if( !mxPieFmt )
1467 mxPieFmt = pGroupFmt->mxPieFmt;
1468 if( !mxSeriesFmt )
1469 mxSeriesFmt = pGroupFmt->mxSeriesFmt;
1470 if( !mx3dDataFmt )
1471 mx3dDataFmt = pGroupFmt->mx3dDataFmt;
1472 if( !mxAttLabel )
1473 mxAttLabel = pGroupFmt->mxAttLabel;
1474 }
1475
1476 /* Create missing but required formats. Existing line, area, and marker
1477 format objects are needed to create automatic series formatting. */
1478 if( !mxLineFmt )
1480 if( !mxAreaFmt && !mxEscherFmt )
1481 mxAreaFmt = std::make_shared<XclImpChAreaFormat>();
1482 if( !mxMarkerFmt )
1483 mxMarkerFmt = std::make_shared<XclImpChMarkerFormat>();
1484
1485 // remove formats not used for the current chart type
1486 RemoveUnusedFormats( rTypeInfo );
1487 // update data label
1488 UpdateDataLabel( pGroupFmt );
1489}
1490
1492{
1493 // remove formats if they are automatic in this and in the passed series format
1494 if( pSeriesFmt )
1495 {
1496 if( IsAutoLine() && pSeriesFmt->IsAutoLine() )
1497 mxLineFmt.clear();
1498 if( IsAutoArea() && pSeriesFmt->IsAutoArea() )
1499 mxAreaFmt.reset();
1500 if( IsAutoMarker() && pSeriesFmt->IsAutoMarker() )
1501 mxMarkerFmt.reset();
1502 mxSeriesFmt.reset();
1503 }
1504
1505 // Excel ignores 3D bar format for single data points
1506 mx3dDataFmt.reset();
1507 // remove point line formats for linear chart types, TODO: implement in OOChart
1508 if( !rTypeInfo.IsSeriesFrameFormat() )
1509 mxLineFmt.clear();
1510
1511 // remove formats not used for the current chart type
1512 RemoveUnusedFormats( rTypeInfo );
1513 // update data label
1514 UpdateDataLabel( pSeriesFmt );
1515}
1516
1518{
1519 if( !mxLineFmt )
1521 mxAreaFmt.reset();
1522 mxEscherFmt.reset();
1523 mxMarkerFmt.reset();
1524 mxPieFmt.reset();
1525 mxSeriesFmt.reset();
1526 mx3dDataFmt.reset();
1527 mxAttLabel.reset();
1528 // update data label
1529 UpdateDataLabel( nullptr );
1530}
1531
1532void XclImpChDataFormat::Convert( ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet ) const
1533{
1534 /* Line and area format.
1535 #i71810# If the data points are filled with bitmaps, textures, or
1536 patterns, then only bar charts will use the CHPICFORMAT record to
1537 determine stacking/stretching mode. All other chart types ignore this
1538 record and always use the property 'fill-type' from the DFF property
1539 set (stretched for bitmaps, and stacked for textures and patterns). */
1540 bool bUsePicFmt = rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR;
1541 ConvertFrameBase( GetChRoot(), rPropSet, rTypeInfo.GetSeriesObjectType(), maData.mnFormatIdx, bUsePicFmt );
1542
1543 // #i83151# only hair lines in 3D charts with filled data points
1544 if( rTypeInfo.mb3dChart && rTypeInfo.IsSeriesFrameFormat() && mxLineFmt && mxLineFmt->HasLine() )
1545 rPropSet.SetProperty< sal_Int32 >( "BorderWidth", 0 );
1546
1547 // other formatting
1548 if( mxMarkerFmt )
1549 mxMarkerFmt->Convert( GetChRoot(), rPropSet, maData.mnFormatIdx, GetLineWeight() );
1550 if( mxPieFmt )
1551 mxPieFmt->Convert( rPropSet );
1552 if( mx3dDataFmt )
1553 mx3dDataFmt->Convert( rPropSet );
1554 if( mxLabel )
1555 mxLabel->ConvertDataLabel( rPropSet, rTypeInfo, pGlobalPropSet );
1556
1557 // 3D settings
1558 rPropSet.SetProperty< sal_Int16 >( EXC_CHPROP_PERCENTDIAGONAL, 0 );
1559
1560 /* Special case: set marker color as line color, if series line is not
1561 visible. This makes the color visible in the marker area.
1562 TODO: remove this if OOChart supports own colors in markers. */
1563 if( !rTypeInfo.IsSeriesFrameFormat() && !HasLine() && mxMarkerFmt )
1564 mxMarkerFmt->ConvertColor( GetChRoot(), rPropSet, maData.mnFormatIdx );
1565}
1566
1568{
1569 ConvertLineBase( GetChRoot(), rPropSet, eObjType );
1570}
1571
1572void XclImpChDataFormat::ConvertArea( ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const
1573{
1574 ConvertAreaBase( GetChRoot(), rPropSet, EXC_CHOBJTYPE_FILLEDSERIES, nFormatIdx );
1575}
1576
1578{
1579 // data point marker only in linear 2D charts
1580 if( rTypeInfo.IsSeriesFrameFormat() )
1581 mxMarkerFmt.reset();
1582 // pie format only in pie/donut charts
1583 if( rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE )
1584 mxPieFmt.reset();
1585 // 3D format only in 3D bar charts
1586 if( !rTypeInfo.mb3dChart || (rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_BAR) )
1587 mx3dDataFmt.reset();
1588}
1589
1591{
1592 /* CHTEXT groups linked to data labels override existing CHATTACHEDLABEL
1593 records. Only if there is a CHATTACHEDLABEL record without a CHTEXT
1594 group, the contents of the CHATTACHEDLABEL record are used. In this
1595 case a new CHTEXT group is created and filled with the settings from
1596 the CHATTACHEDLABEL record. */
1597 const XclImpChText* pDefText = nullptr;
1598 if (pParentFmt)
1599 pDefText = pParentFmt->GetDataLabel();
1600 if (!pDefText)
1602 if (mxLabel)
1603 mxLabel->UpdateText(pDefText);
1604 else if (mxAttLabel)
1605 mxLabel = mxAttLabel->CreateDataLabel( pDefText );
1606}
1607
1609 XclImpChRoot( rRoot )
1610{
1611}
1612
1614{
1615 maData.mnLineType = rStrm.ReaduInt8();
1616 maData.mnOrder = rStrm.ReaduInt8();
1618 maData.mnShowEquation = rStrm.ReaduInt8();
1619 maData.mnShowRSquared = rStrm.ReaduInt8();
1622}
1623
1624Reference< XRegressionCurve > XclImpChSerTrendLine::CreateRegressionCurve() const
1625{
1626 // trend line type
1627 Reference< XRegressionCurve > xRegCurve;
1628 switch( maData.mnLineType )
1629 {
1631 if( maData.mnOrder == 1 )
1632 {
1633 xRegCurve = LinearRegressionCurve::create( comphelper::getProcessComponentContext() );
1634 } else {
1635 xRegCurve = PolynomialRegressionCurve::create( comphelper::getProcessComponentContext() );
1636 }
1637 break;
1639 xRegCurve = ExponentialRegressionCurve::create( comphelper::getProcessComponentContext() );
1640 break;
1642 xRegCurve = LogarithmicRegressionCurve::create( comphelper::getProcessComponentContext() );
1643 break;
1645 xRegCurve = PotentialRegressionCurve::create( comphelper::getProcessComponentContext() );
1646 break;
1648 xRegCurve = MovingAverageRegressionCurve::create( comphelper::getProcessComponentContext() );
1649 break;
1650 }
1651
1652 // trend line formatting
1653 if( xRegCurve.is() && mxDataFmt )
1654 {
1655 ScfPropertySet aPropSet( xRegCurve );
1656 mxDataFmt->ConvertLine( aPropSet, EXC_CHOBJTYPE_TRENDLINE );
1657
1659 aPropSet.SetProperty(EXC_CHPROP_POLYNOMIAL_DEGREE, static_cast<sal_Int32> (maData.mnOrder) );
1660 aPropSet.SetProperty(EXC_CHPROP_MOVING_AVERAGE_PERIOD, static_cast<sal_Int32> (maData.mnOrder) );
1663
1664 bool bForceIntercept = std::isfinite(maData.mfIntercept);
1665 aPropSet.SetProperty(EXC_CHPROP_FORCE_INTERCEPT, bForceIntercept);
1666 if (bForceIntercept)
1667 {
1669 }
1670
1671 // #i83100# show equation and correlation coefficient
1672 ScfPropertySet aLabelProp( xRegCurve->getEquationProperties() );
1675
1676 // #i83100# formatting of the equation text box
1677 if (const XclImpChText* pLabel = mxDataFmt->GetDataLabel())
1678 {
1679 pLabel->ConvertFont( aLabelProp );
1680 pLabel->ConvertFrame( aLabelProp );
1681 pLabel->ConvertNumFmt( aLabelProp, false );
1682 }
1683 }
1684
1685 return xRegCurve;
1686}
1687
1689 XclImpChRoot( rRoot )
1690{
1691}
1692
1694{
1695 maData.mnBarType = rStrm.ReaduInt8();
1696 maData.mnSourceType = rStrm.ReaduInt8();
1697 maData.mnLineEnd = rStrm.ReaduInt8();
1698 rStrm.Ignore( 1 );
1700 maData.mnValueCount = rStrm.ReaduInt16();
1701}
1702
1704{
1705 mxValueLink = xValueLink;
1706 mxDataFmt = xDataFmt;
1707}
1708
1709Reference< XLabeledDataSequence > XclImpChSerErrorBar::CreateValueSequence() const
1710{
1711 return lclCreateLabeledDataSequence( mxValueLink, XclChartHelper::GetErrorBarValuesRole( maData.mnBarType ) );
1712}
1713
1714Reference< XPropertySet > XclImpChSerErrorBar::CreateErrorBar( const XclImpChSerErrorBar* pPosBar, const XclImpChSerErrorBar* pNegBar )
1715{
1716 Reference< XPropertySet > xErrorBar;
1717
1718 if( const XclImpChSerErrorBar* pPrimaryBar = pPosBar ? pPosBar : pNegBar )
1719 {
1720 xErrorBar.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_ERRORBAR ), UNO_QUERY );
1721 ScfPropertySet aBarProp( xErrorBar );
1722
1723 // plus/minus bars visible?
1724 aBarProp.SetBoolProperty( EXC_CHPROP_SHOWPOSITIVEERROR, pPosBar != nullptr );
1725 aBarProp.SetBoolProperty( EXC_CHPROP_SHOWNEGATIVEERROR, pNegBar != nullptr );
1726
1727 // type of displayed error
1728 switch( pPrimaryBar->maData.mnSourceType )
1729 {
1731 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::RELATIVE );
1732 aBarProp.SetProperty( EXC_CHPROP_POSITIVEERROR, pPrimaryBar->maData.mfValue );
1733 aBarProp.SetProperty( EXC_CHPROP_NEGATIVEERROR, pPrimaryBar->maData.mfValue );
1734 break;
1735 case EXC_CHSERERR_FIXED:
1736 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::ABSOLUTE );
1737 aBarProp.SetProperty( EXC_CHPROP_POSITIVEERROR, pPrimaryBar->maData.mfValue );
1738 aBarProp.SetProperty( EXC_CHPROP_NEGATIVEERROR, pPrimaryBar->maData.mfValue );
1739 break;
1741 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::STANDARD_DEVIATION );
1742 aBarProp.SetProperty( EXC_CHPROP_WEIGHT, pPrimaryBar->maData.mfValue );
1743 break;
1745 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::STANDARD_ERROR );
1746 break;
1748 {
1749 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::FROM_DATA );
1750 // attach data sequences to error bar
1751 Reference< XDataSink > xDataSink( xErrorBar, UNO_QUERY );
1752 if( xDataSink.is() )
1753 {
1754 // create vector of all value sequences
1755 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
1756 // add positive values
1757 if( pPosBar )
1758 {
1759 Reference< XLabeledDataSequence > xValueSeq = pPosBar->CreateValueSequence();
1760 if( xValueSeq.is() )
1761 aLabeledSeqVec.push_back( xValueSeq );
1762 }
1763 // add negative values
1764 if( pNegBar )
1765 {
1766 Reference< XLabeledDataSequence > xValueSeq = pNegBar->CreateValueSequence();
1767 if( xValueSeq.is() )
1768 aLabeledSeqVec.push_back( xValueSeq );
1769 }
1770 // attach labeled data sequences to series
1771 if( aLabeledSeqVec.empty() )
1772 xErrorBar.clear();
1773 else
1774 xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
1775 }
1776 }
1777 break;
1778 default:
1779 xErrorBar.clear();
1780 }
1781
1782 // error bar formatting
1783 if( pPrimaryBar->mxDataFmt && xErrorBar.is() )
1784 pPrimaryBar->mxDataFmt->ConvertLine( aBarProp, EXC_CHOBJTYPE_ERRORBAR );
1785 }
1786
1787 return xErrorBar;
1788}
1789
1790XclImpChSeries::XclImpChSeries( const XclImpChRoot& rRoot, sal_uInt16 nSeriesIdx ) :
1791 XclImpChRoot( rRoot ),
1792 mnGroupIdx( EXC_CHSERGROUP_NONE ),
1793 mnSeriesIdx( nSeriesIdx ),
1794 mnParentIdx( EXC_CHSERIES_INVALID ),
1795 mbLabelDeleted( false )
1796{
1797}
1798
1800{
1801 maData.mnCategType = rStrm.ReaduInt16();
1802 maData.mnValueType = rStrm.ReaduInt16();
1803 maData.mnCategCount = rStrm.ReaduInt16();
1804 maData.mnValueCount = rStrm.ReaduInt16();
1805 if( GetBiff() == EXC_BIFF8 )
1806 {
1807 maData.mnBubbleType = rStrm.ReaduInt16();
1808 maData.mnBubbleCount = rStrm.ReaduInt16();
1809 }
1810}
1811
1813{
1814 switch( rStrm.GetRecId() )
1815 {
1818 break;
1821 break;
1822 case EXC_ID_CHSERGROUP:
1823 mnGroupIdx = rStrm.ReaduInt16();
1824 break;
1825 case EXC_ID_CHSERPARENT:
1827 break;
1830 break;
1833 break;
1836 break;
1837 }
1838}
1839
1841{
1842 if (!xDataFmt)
1843 return;
1844
1845 sal_uInt16 nPointIdx = xDataFmt->GetPointPos().mnPointIdx;
1846 if (nPointIdx == EXC_CHDATAFORMAT_ALLPOINTS)
1847 {
1848 if (mxSeriesFmt)
1849 // Don't overwrite the existing format.
1850 return;
1851
1852 mxSeriesFmt = xDataFmt;
1853 if (HasParentSeries())
1854 return;
1855
1857 if (pTypeGroup)
1858 pTypeGroup->SetUsedFormatIndex(xDataFmt->GetFormatIdx());
1859
1860 return;
1861 }
1862
1863 if (nPointIdx >= EXC_CHDATAFORMAT_MAXPOINTCOUNT)
1864 // Above the max point count. Bail out.
1865 return;
1866
1867 XclImpChDataFormatMap::iterator itr = maPointFmts.lower_bound(nPointIdx);
1868 if (itr == maPointFmts.end() || maPointFmts.key_comp()(nPointIdx, itr->first))
1869 {
1870 // No object exists at this point index position. Insert it.
1871 itr = maPointFmts.insert(itr, XclImpChDataFormatMap::value_type(nPointIdx, xDataFmt));
1872 }
1873}
1874
1876{
1877 if (!xLabel)
1878 return;
1879
1880 sal_uInt16 nPointIdx = xLabel->GetPointPos().mnPointIdx;
1881 if ((nPointIdx != EXC_CHDATAFORMAT_ALLPOINTS) && (nPointIdx >= EXC_CHDATAFORMAT_MAXPOINTCOUNT))
1882 // Above the maximum allowed data points. Bail out.
1883 return;
1884
1885 XclImpChTextMap::iterator itr = maLabels.lower_bound(nPointIdx);
1886 if (itr == maLabels.end() || maLabels.key_comp()(nPointIdx, itr->first))
1887 {
1888 // No object exists at this point index position. Insert it.
1889 itr = maLabels.insert(itr, XclImpChTextMap::value_type(nPointIdx, xLabel));
1890 }
1891}
1892
1894{
1895 OSL_ENSURE( !HasParentSeries(), "XclImpChSeries::AddChildSeries - not allowed for child series" );
1896 if (&rSeries == this)
1897 {
1898 SAL_WARN("sc.filter", "self add attempt");
1899 return;
1900 }
1901
1902 /* In Excel, trend lines and error bars are stored as own series. In Calc,
1903 these are properties of the parent series. This function adds the
1904 settings of the passed series to this series. */
1905 maTrendLines.insert( maTrendLines.end(), rSeries.maTrendLines.begin(), rSeries.maTrendLines.end() );
1906 for (auto const& it : rSeries.m_ErrorBars)
1907 {
1908 m_ErrorBars.insert(std::make_pair(it.first, std::make_unique<XclImpChSerErrorBar>(*it.second)));
1909 }
1910}
1911
1913{
1914 if( HasParentSeries() )
1915 {
1916 // *** series is a child series, e.g. trend line or error bar ***
1917
1918 // create missing series format
1919 if( !mxSeriesFmt )
1921
1922 if( mxSeriesFmt )
1923 {
1924 // #i83100# set text label format, e.g. for trend line equations
1925 XclImpChTextRef xLabel;
1926 XclImpChTextMap::iterator itr = maLabels.find(EXC_CHDATAFORMAT_ALLPOINTS);
1927 if (itr != maLabels.end())
1928 xLabel = itr->second;
1929 mxSeriesFmt->SetDataLabel(xLabel);
1930 // create missing automatic formats
1931 mxSeriesFmt->UpdateTrendLineFormat();
1932 }
1933
1934 // copy series formatting to child objects
1935 for (auto const& trendLine : maTrendLines)
1936 {
1937 trendLine->SetDataFormat(mxSeriesFmt);
1938 if (mxTitleLink && mxTitleLink->HasString())
1939 {
1940 trendLine->SetTrendlineName(mxTitleLink->GetString());
1941 }
1942 }
1943 for (auto const& it : m_ErrorBars)
1944 {
1945 it.second->SetSeriesData( mxValueLink, mxSeriesFmt );
1946 }
1947 }
1948 else if( XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() )
1949 {
1950 // *** series is a regular data series ***
1951
1952 // create missing series format
1953 if( !mxSeriesFmt )
1954 {
1955 // #i51639# use a new unused format index to create series default format
1956 sal_uInt16 nFormatIdx = pTypeGroup->PopUnusedFormatIndex();
1958 }
1959
1960 // set text labels to data formats
1961 for (auto const& label : maLabels)
1962 {
1963 sal_uInt16 nPointIdx = label.first;
1964 if (nPointIdx == EXC_CHDATAFORMAT_ALLPOINTS)
1965 {
1966 if (!mxSeriesFmt)
1968 mxSeriesFmt->SetDataLabel(label.second);
1969 }
1970 else if (nPointIdx < EXC_CHDATAFORMAT_MAXPOINTCOUNT)
1971 {
1973 XclImpChDataFormatMap::iterator itr = maPointFmts.lower_bound(nPointIdx);
1974 if (itr == maPointFmts.end() || maPointFmts.key_comp()(nPointIdx, itr->first))
1975 {
1976 // No object exists at this point index position. Insert
1977 // a new one.
1979 itr = maPointFmts.insert(
1980 itr, XclImpChDataFormatMap::value_type(nPointIdx, p));
1981 }
1982 else
1983 p = itr->second;
1984 p->SetDataLabel(label.second);
1985 }
1986 }
1987
1988 // update series format (copy missing formatting from group default format)
1989 if( mxSeriesFmt )
1990 mxSeriesFmt->UpdateSeriesFormat( pTypeGroup->GetTypeInfo(), pTypeGroup->GetGroupFormat().get() );
1991
1992 // update data point formats (removes unchanged automatic formatting)
1993 for (auto const& pointFormat : maPointFmts)
1994 pointFormat.second->UpdatePointFormat( pTypeGroup->GetTypeInfo(), mxSeriesFmt.get() );
1995 }
1996}
1997
1998namespace {
1999
2001ScfPropertySet lclGetPointPropSet( Reference< XDataSeries > const & xDataSeries, sal_uInt16 nPointIdx )
2002{
2003 ScfPropertySet aPropSet;
2004 try
2005 {
2006 aPropSet.Set( xDataSeries->getDataPointByIndex( static_cast< sal_Int32 >( nPointIdx ) ) );
2007 }
2008 catch( Exception& )
2009 {
2010 OSL_FAIL( "lclGetPointPropSet - no data point property set" );
2011 }
2012 return aPropSet;
2013}
2014
2015} // namespace
2016
2017Reference< XLabeledDataSequence > XclImpChSeries::CreateValueSequence( const OUString& rValueRole ) const
2018{
2019 return lclCreateLabeledDataSequence( mxValueLink, rValueRole, mxTitleLink.get() );
2020}
2021
2022Reference< XLabeledDataSequence > XclImpChSeries::CreateCategSequence( const OUString& rCategRole ) const
2023{
2024 return lclCreateLabeledDataSequence( mxCategLink, rCategRole );
2025}
2026
2027Reference< XDataSeries > XclImpChSeries::CreateDataSeries() const
2028{
2029 Reference< XDataSeries > xDataSeries;
2030 if( const XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() )
2031 {
2032 const XclChExtTypeInfo& rTypeInfo = pTypeGroup->GetTypeInfo();
2033
2034 // create the data series object
2035 xDataSeries.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_DATASERIES ), UNO_QUERY );
2036
2037 // attach data and title sequences to series
2038 Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
2039 if( xDataSink.is() )
2040 {
2041 // create vector of all value sequences
2042 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
2043 // add Y values
2044 Reference< XLabeledDataSequence > xYValueSeq =
2046 if( xYValueSeq.is() )
2047 aLabeledSeqVec.push_back( xYValueSeq );
2048 // add X values
2049 if( !rTypeInfo.mbCategoryAxis )
2050 {
2051 Reference< XLabeledDataSequence > xXValueSeq =
2053 if( xXValueSeq.is() )
2054 aLabeledSeqVec.push_back( xXValueSeq );
2055 // add size values of bubble charts
2056 if( rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES )
2057 {
2058 Reference< XLabeledDataSequence > xSizeValueSeq =
2059 lclCreateLabeledDataSequence( mxBubbleLink, EXC_CHPROP_ROLE_SIZEVALUES, mxTitleLink.get() );
2060 if( xSizeValueSeq.is() )
2061 aLabeledSeqVec.push_back( xSizeValueSeq );
2062 }
2063 }
2064 // attach labeled data sequences to series
2065 if( !aLabeledSeqVec.empty() )
2066 xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
2067 }
2068
2069 // series formatting
2070 ScfPropertySet aSeriesProp( xDataSeries );
2071 if( mxSeriesFmt )
2072 mxSeriesFmt->Convert( aSeriesProp, rTypeInfo );
2073
2074 if (mbLabelDeleted)
2075 aSeriesProp.SetProperty(EXC_CHPROP_SHOWLEGENDENTRY, false);
2076
2077 // trend lines
2078 ConvertTrendLines( xDataSeries );
2079
2080 // error bars
2081 Reference< XPropertySet > xErrorBarX = CreateErrorBar( EXC_CHSERERR_XPLUS, EXC_CHSERERR_XMINUS );
2082 if( xErrorBarX.is() )
2083 aSeriesProp.SetProperty( EXC_CHPROP_ERRORBARX, xErrorBarX );
2084 Reference< XPropertySet > xErrorBarY = CreateErrorBar( EXC_CHSERERR_YPLUS, EXC_CHSERERR_YMINUS );
2085 if( xErrorBarY.is() )
2086 aSeriesProp.SetProperty( EXC_CHPROP_ERRORBARY, xErrorBarY );
2087
2088 // own area formatting for every data point (TODO: varying line color not supported)
2089 bool bVarPointFmt = pTypeGroup->HasVarPointFormat() && rTypeInfo.IsSeriesFrameFormat();
2091 // #i91271# always set area formatting for every point in pie/doughnut charts
2092 if (mxSeriesFmt && mxValueLink && ((bVarPointFmt && mxSeriesFmt->IsAutoArea()) || (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE)))
2093 {
2094 for( sal_uInt16 nPointIdx = 0, nPointCount = mxValueLink->GetCellCount(); nPointIdx < nPointCount; ++nPointIdx )
2095 {
2096 ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIdx );
2097 mxSeriesFmt->ConvertArea( aPointProp, bVarPointFmt ? nPointIdx : mnSeriesIdx );
2098 }
2099 }
2100
2101 // data point formatting
2102 for (auto const& pointFormat : maPointFmts)
2103 {
2104 ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, pointFormat.first );
2105 pointFormat.second->Convert( aPointProp, rTypeInfo, &aSeriesProp );
2106 }
2107 }
2108 return xDataSeries;
2109}
2110
2111void XclImpChSeries::FillAllSourceLinks( ::std::vector< ScTokenRef >& rTokens ) const
2112{
2113 if( mxValueLink )
2114 mxValueLink->FillSourceLink( rTokens );
2115 if( mxCategLink )
2116 mxCategLink->FillSourceLink( rTokens );
2117 if( mxTitleLink )
2118 mxTitleLink->FillSourceLink( rTokens );
2119 if( mxBubbleLink )
2120 mxBubbleLink->FillSourceLink( rTokens );
2121}
2122
2124{
2125 XclImpChSourceLinkRef xSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
2126 xSrcLink->ReadChSourceLink( rStrm );
2127 switch( xSrcLink->GetDestType() )
2128 {
2129 case EXC_CHSRCLINK_TITLE: mxTitleLink = xSrcLink; break;
2130 case EXC_CHSRCLINK_VALUES: mxValueLink = xSrcLink; break;
2131 case EXC_CHSRCLINK_CATEGORY: mxCategLink = xSrcLink; break;
2132 case EXC_CHSRCLINK_BUBBLES: mxBubbleLink = xSrcLink; break;
2133 }
2134}
2135
2137{
2138 // #i51639# chart stores all data formats and assigns them later to the series
2140}
2141
2143{
2144 mnParentIdx = rStrm.ReaduInt16();
2145 // index to parent series is 1-based, convert it to 0-based
2146 if( mnParentIdx > 0 )
2147 --mnParentIdx;
2148 else
2150}
2151
2153{
2154 XclImpChSerTrendLineRef xTrendLine = std::make_shared<XclImpChSerTrendLine>( GetChRoot() );
2155 xTrendLine->ReadChSerTrendLine( rStrm );
2156 maTrendLines.push_back( xTrendLine );
2157}
2158
2160{
2161 unique_ptr<XclImpChSerErrorBar> pErrorBar(new XclImpChSerErrorBar(GetChRoot()));
2162 pErrorBar->ReadChSerErrorBar(rStrm);
2163 sal_uInt8 nBarType = pErrorBar->GetBarType();
2164 m_ErrorBars.insert(std::make_pair(nBarType, std::move(pErrorBar)));
2165}
2166
2167XclImpChDataFormatRef XclImpChSeries::CreateDataFormat( sal_uInt16 nPointIdx, sal_uInt16 nFormatIdx )
2168{
2169 XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
2170 xDataFmt->SetPointPos( XclChDataPointPos( mnSeriesIdx, nPointIdx ), nFormatIdx );
2171 return xDataFmt;
2172}
2173
2174void XclImpChSeries::ConvertTrendLines( Reference< XDataSeries > const & xDataSeries ) const
2175{
2176 Reference< XRegressionCurveContainer > xRegCurveCont( xDataSeries, UNO_QUERY );
2177 if( !xRegCurveCont.is() )
2178 return;
2179
2180 for (auto const& trendLine : maTrendLines)
2181 {
2182 try
2183 {
2184 Reference< XRegressionCurve > xRegCurve = trendLine->CreateRegressionCurve();
2185 if( xRegCurve.is() )
2186 {
2187 xRegCurveCont->addRegressionCurve( xRegCurve );
2188 }
2189 }
2190 catch( Exception& )
2191 {
2192 OSL_FAIL( "XclImpChSeries::ConvertTrendLines - cannot add regression curve" );
2193 }
2194 }
2195}
2196
2197Reference< XPropertySet > XclImpChSeries::CreateErrorBar( sal_uInt8 nPosBarId, sal_uInt8 nNegBarId ) const
2198{
2199 XclImpChSerErrorBarMap::const_iterator itrPosBar = m_ErrorBars.find(nPosBarId);
2200 XclImpChSerErrorBarMap::const_iterator itrNegBar = m_ErrorBars.find(nNegBarId);
2201 XclImpChSerErrorBarMap::const_iterator itrEnd = m_ErrorBars.end();
2202 if (itrPosBar == itrEnd || itrNegBar == itrEnd)
2203 return Reference<XPropertySet>();
2204
2205 return XclImpChSerErrorBar::CreateErrorBar(itrPosBar->second.get(), itrNegBar->second.get());
2206}
2207
2209{
2210 rStrm.Ignore(2);
2211 sal_uInt16 nFlags = rStrm.ReaduInt16();
2213}
2214
2215// Chart type groups ==========================================================
2216
2218 XclImpChRoot( rRoot ),
2219 mnRecId( EXC_ID_CHUNKNOWN ),
2220 maTypeInfo( rRoot.GetChartTypeInfo( EXC_CHTYPEID_UNKNOWN ) )
2221{
2222}
2223
2225{
2226 sal_uInt16 nRecId = rStrm.GetRecId();
2227 bool bKnownType = true;
2228
2229 switch( nRecId )
2230 {
2231 case EXC_ID_CHBAR:
2234 maData.mnFlags = rStrm.ReaduInt16();
2235 break;
2236
2237 case EXC_ID_CHLINE:
2238 case EXC_ID_CHAREA:
2239 case EXC_ID_CHRADARLINE:
2240 case EXC_ID_CHRADARAREA:
2241 maData.mnFlags = rStrm.ReaduInt16();
2242 break;
2243
2244 case EXC_ID_CHPIE:
2245 maData.mnRotation = rStrm.ReaduInt16();
2246 maData.mnPieHole = rStrm.ReaduInt16();
2247 if( GetBiff() == EXC_BIFF8 )
2248 maData.mnFlags = rStrm.ReaduInt16();
2249 else
2250 maData.mnFlags = 0;
2251 break;
2252
2253 case EXC_ID_CHPIEEXT:
2254 maData.mnRotation = 0;
2255 maData.mnPieHole = 0;
2256 maData.mnFlags = 0;
2257 break;
2258
2259 case EXC_ID_CHSCATTER:
2260 if( GetBiff() == EXC_BIFF8 )
2261 {
2262 maData.mnBubbleSize = rStrm.ReaduInt16();
2263 maData.mnBubbleType = rStrm.ReaduInt16();
2264 maData.mnFlags = rStrm.ReaduInt16();
2265 }
2266 else
2267 maData.mnFlags = 0;
2268 break;
2269
2270 case EXC_ID_CHSURFACE:
2271 maData.mnFlags = rStrm.ReaduInt16();
2272 break;
2273
2274 default:
2275 bKnownType = false;
2276 }
2277
2278 if( bKnownType )
2279 mnRecId = nRecId;
2280}
2281
2282void XclImpChType::Finalize( bool bStockChart )
2283{
2284 switch( mnRecId )
2285 {
2286 case EXC_ID_CHLINE:
2287 maTypeInfo = GetChartTypeInfo( bStockChart ?
2289 break;
2290 case EXC_ID_CHBAR:
2294 break;
2295 case EXC_ID_CHPIE:
2298 break;
2299 case EXC_ID_CHSCATTER:
2303 break;
2304 default:
2306 }
2307
2308 switch( maTypeInfo.meTypeId )
2309 {
2315 break;
2316 default:;
2317 }
2318}
2319
2321{
2322 bool bStacked = false;
2324 {
2326 bStacked =
2329 break;
2331 bStacked =
2334 break;
2335 default:;
2336 }
2337 return bStacked;
2338}
2339
2341{
2342 bool bPercent = false;
2344 {
2346 bPercent =
2349 break;
2351 bPercent =
2354 break;
2355 default:;
2356 }
2357 return bPercent;
2358}
2359
2361{
2362 // radar charts disable category labels in chart type, not in CHTICK of X axis
2364}
2365
2366Reference< XCoordinateSystem > XclImpChType::CreateCoordSystem( bool b3dChart ) const
2367{
2368 // create the coordinate system object
2369 Reference< css::uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
2370 Reference< XCoordinateSystem > xCoordSystem;
2372 {
2373 if( b3dChart )
2374 xCoordSystem = css::chart2::PolarCoordinateSystem3d::create(xContext);
2375 else
2376 xCoordSystem = css::chart2::PolarCoordinateSystem2d::create(xContext);
2377 }
2378 else
2379 {
2380 if( b3dChart )
2381 xCoordSystem = css::chart2::CartesianCoordinateSystem3d::create(xContext);
2382 else
2383 xCoordSystem = css::chart2::CartesianCoordinateSystem2d::create(xContext);
2384 }
2385
2386 // swap X and Y axis
2388 {
2389 ScfPropertySet aCoordSysProp( xCoordSystem );
2390 aCoordSysProp.SetBoolProperty( EXC_CHPROP_SWAPXANDYAXIS, true );
2391 }
2392
2393 return xCoordSystem;
2394}
2395
2396Reference< XChartType > XclImpChType::CreateChartType( Reference< XDiagram > const & xDiagram, bool b3dChart ) const
2397{
2398 OUString aService = OUString::createFromAscii( maTypeInfo.mpcServiceName );
2399 Reference< XChartType > xChartType( ScfApiHelper::CreateInstance( aService ), UNO_QUERY );
2400
2401 // additional properties
2402 switch( maTypeInfo.meTypeCateg )
2403 {
2405 {
2406 ScfPropertySet aTypeProp( xChartType );
2407 Sequence< sal_Int32 > aInt32Seq{ -maData.mnOverlap, -maData.mnOverlap };
2408 aTypeProp.SetProperty( EXC_CHPROP_OVERLAPSEQ, aInt32Seq );
2409 aInt32Seq = { maData.mnGap, maData.mnGap };
2410 aTypeProp.SetProperty( EXC_CHPROP_GAPWIDTHSEQ, aInt32Seq );
2411 }
2412 break;
2414 {
2415 ScfPropertySet aTypeProp( xChartType );
2417 /* #i85166# starting angle of first pie slice. 3D pie charts use Y
2418 rotation setting in view3D element. Of-pie charts do not
2419 support pie rotation. */
2420 if( !b3dChart && (maTypeInfo.meTypeId != EXC_CHTYPEID_PIEEXT) )
2421 {
2422 ScfPropertySet aDiaProp( xDiagram );
2424 }
2425 }
2426 break;
2427 default:;
2428 }
2429
2430 return xChartType;
2431}
2432
2434{
2435 maData.mnRotation = rStrm.ReaduInt16();
2437 maData.mnEyeDist = rStrm.ReaduInt16();
2438 maData.mnRelHeight = rStrm.ReaduInt16();
2439 maData.mnRelDepth = rStrm.ReaduInt16();
2440 maData.mnDepthGap = rStrm.ReaduInt16();
2441 maData.mnFlags = rStrm.ReaduInt16();
2442}
2443
2444void XclImpChChart3d::Convert( ScfPropertySet& rPropSet, bool b3dWallChart ) const
2445{
2446 namespace cssd = ::com::sun::star::drawing;
2447
2448// #i104057# do not assert this, written by broken external generators
2449// OSL_ENSURE( ::get_flag( maData.mnFlags, EXC_CHCHART3D_HASWALLS ) == b3dWallChart, "XclImpChChart3d::Convert - wrong wall flag" );
2450
2451 sal_Int32 nRotationY = 0;
2452 sal_Int32 nRotationX = 0;
2453 sal_Int32 nPerspective = 15;
2454 bool bRightAngled = false;
2455 cssd::ProjectionMode eProjMode = cssd::ProjectionMode_PERSPECTIVE;
2456 Color aAmbientColor, aLightColor;
2457
2458 if( b3dWallChart )
2459 {
2460 // Y rotation (Excel [0..359], Chart2 [-179,180])
2461 nRotationY = NormAngle180<sal_Int32>(maData.mnRotation);
2462 // X rotation a.k.a. elevation (Excel [-90..90], Chart2 [-179,180])
2463 nRotationX = limit_cast< sal_Int32, sal_Int32 >( maData.mnElevation, -90, 90 );
2464 // perspective (Excel and Chart2 [0,100])
2465 nPerspective = limit_cast< sal_Int32, sal_Int32 >( maData.mnEyeDist, 0, 100 );
2466 // right-angled axes
2467 bRightAngled = !::get_flag( maData.mnFlags, EXC_CHCHART3D_REAL3D );
2468 // projection mode (parallel axes, if right-angled, #i90360# or if perspective is at 0%)
2469 bool bParallel = bRightAngled || (nPerspective == 0);
2470 eProjMode = bParallel ? cssd::ProjectionMode_PARALLEL : cssd::ProjectionMode_PERSPECTIVE;
2471 // ambient color (Gray 20%)
2472 aAmbientColor = Color( 204, 204, 204 );
2473 // light color (Gray 60%)
2474 aLightColor = Color( 102, 102, 102 );
2475 }
2476 else
2477 {
2478 // Y rotation not used in pie charts, but 'first pie slice angle'
2479 nRotationY = 0;
2481 // X rotation a.k.a. elevation (map Excel [10..80] to Chart2 [-80,-10])
2482 nRotationX = limit_cast< sal_Int32, sal_Int32 >( maData.mnElevation, 10, 80 ) - 90;
2483 // perspective (Excel and Chart2 [0,100])
2484 nPerspective = limit_cast< sal_Int32, sal_Int32 >( maData.mnEyeDist, 0, 100 );
2485 // no right-angled axes in pie charts, but parallel projection
2486 bRightAngled = false;
2487 eProjMode = cssd::ProjectionMode_PARALLEL;
2488 // ambient color (Gray 30%)
2489 aAmbientColor = Color( 179, 179, 179 );
2490 // light color (Gray 70%)
2491 aLightColor = Color( 76, 76, 76 );
2492 }
2493
2494 // properties
2495 rPropSet.SetProperty( EXC_CHPROP_3DRELATIVEHEIGHT, static_cast<sal_Int32>(maData.mnRelHeight / 2)); // seems to be 200%, change to 100%
2496 rPropSet.SetProperty( EXC_CHPROP_ROTATIONVERTICAL, nRotationY );
2497 rPropSet.SetProperty( EXC_CHPROP_ROTATIONHORIZONTAL, nRotationX );
2498 rPropSet.SetProperty( EXC_CHPROP_PERSPECTIVE, nPerspective );
2499 rPropSet.SetBoolProperty( EXC_CHPROP_RIGHTANGLEDAXES, bRightAngled );
2500 rPropSet.SetProperty( EXC_CHPROP_D3DSCENEPERSPECTIVE, eProjMode );
2501
2502 // light settings
2503 rPropSet.SetProperty( EXC_CHPROP_D3DSCENESHADEMODE, cssd::ShadeMode_FLAT );
2504 rPropSet.SetColorProperty( EXC_CHPROP_D3DSCENEAMBIENTCOLOR, aAmbientColor );
2507 rPropSet.SetColorProperty( EXC_CHPROP_D3DSCENELIGHTCOLOR2, aLightColor );
2508 rPropSet.SetProperty( EXC_CHPROP_D3DSCENELIGHTDIR2, cssd::Direction3D( 0.2, 0.4, 1.0 ) );
2509}
2510
2512 XclImpChRoot( rRoot )
2513{
2514}
2515
2517{
2518 rStrm >> maData.maRect;
2519 maData.mnDockMode = rStrm.ReaduInt8();
2520 maData.mnSpacing = rStrm.ReaduInt8();
2521 maData.mnFlags = rStrm.ReaduInt16();
2522
2523 // trace unsupported features
2524 if( GetTracer().IsEnabled() )
2525 {
2530 }
2531}
2532
2534{
2535 switch( rStrm.GetRecId() )
2536 {
2537 case EXC_ID_CHFRAMEPOS:
2538 mxFramePos = std::make_shared<XclImpChFramePos>();
2539 mxFramePos->ReadChFramePos( rStrm );
2540 break;
2541 case EXC_ID_CHTEXT:
2542 mxText = std::make_shared<XclImpChText>( GetChRoot() );
2543 mxText->ReadRecordGroup( rStrm );
2544 break;
2545 case EXC_ID_CHFRAME:
2546 mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_LEGEND );
2547 mxFrame->ReadRecordGroup( rStrm );
2548 break;
2549 }
2550}
2551
2553{
2554 // legend default formatting differs in OOChart and Excel, missing frame means automatic
2555 if( !mxFrame )
2556 mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_LEGEND );
2557 // Update text formatting. If mxText is empty, the passed default text is used.
2558 lclUpdateText( mxText, GetChartData().GetDefaultText( EXC_CHTEXTTYPE_LEGEND ) );
2559}
2560
2561Reference< XLegend > XclImpChLegend::CreateLegend() const
2562{
2563 Reference< XLegend > xLegend( ScfApiHelper::CreateInstance( SERVICE_CHART2_LEGEND ), UNO_QUERY );
2564 if( xLegend.is() )
2565 {
2566 ScfPropertySet aLegendProp( xLegend );
2567 aLegendProp.SetBoolProperty( EXC_CHPROP_SHOW, true );
2568
2569 // frame properties
2570 if( mxFrame )
2571 mxFrame->Convert( aLegendProp );
2572 // text properties
2573 if( mxText )
2574 mxText->ConvertFont( aLegendProp );
2575
2576 /* Legend position and size. Default positions are used only if the
2577 plot area is positioned automatically (Excel sets the plot area to
2578 manual mode, if the legend is moved or resized). With manual plot
2579 areas, Excel ignores the value in maData.mnDockMode completely. */
2580 cssc2::LegendPosition eApiPos = cssc2::LegendPosition_LINE_END;
2581 cssc::ChartLegendExpansion eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
2582 if( !GetChartData().IsManualPlotArea() ) switch( maData.mnDockMode )
2583 {
2584 case EXC_CHLEGEND_LEFT:
2585 eApiPos = cssc2::LegendPosition_LINE_START;
2586 eApiExpand = cssc::ChartLegendExpansion_HIGH;
2587 break;
2588 case EXC_CHLEGEND_RIGHT:
2589 // top-right not supported
2591 eApiPos = cssc2::LegendPosition_LINE_END;
2592 eApiExpand = cssc::ChartLegendExpansion_HIGH;
2593 break;
2594 case EXC_CHLEGEND_TOP:
2595 eApiPos = cssc2::LegendPosition_PAGE_START;
2596 eApiExpand = cssc::ChartLegendExpansion_WIDE;
2597 break;
2599 eApiPos = cssc2::LegendPosition_PAGE_END;
2600 eApiExpand = cssc::ChartLegendExpansion_WIDE;
2601 break;
2602 }
2603
2604 // no automatic position/size: try to find the correct position and size
2605 if( GetChartData().IsManualPlotArea() || maData.mnDockMode == EXC_CHLEGEND_NOTDOCKED )
2606 {
2607 const XclChFramePos* pFramePos = mxFramePos ? &mxFramePos->GetFramePosData() : nullptr;
2608
2609 /* Legend position. Only the settings from the CHFRAMEPOS record
2610 are used by Excel, the position in the CHLEGEND record will be
2611 ignored. */
2612 if( pFramePos )
2613 {
2614 RelativePosition aRelPos(
2615 CalcRelativeFromChartX( pFramePos->maRect.mnX ),
2616 CalcRelativeFromChartY( pFramePos->maRect.mnY ),
2617 css::drawing::Alignment_TOP_LEFT );
2618 aLegendProp.SetProperty( EXC_CHPROP_RELATIVEPOSITION, aRelPos );
2619 }
2620 else
2621 {
2622 // no manual position/size found, just go for the default
2623 eApiPos = cssc2::LegendPosition_LINE_END;
2624 }
2625
2626 /* Legend size. The member mnBRMode specifies whether size is
2627 automatic or changes manually. Manual size is given in points,
2628 not in chart units. */
2629 if( pFramePos && (pFramePos->mnBRMode == EXC_CHFRAMEPOS_ABSSIZE_POINTS) &&
2630 (pFramePos->maRect.mnWidth > 0) && (pFramePos->maRect.mnHeight > 0) )
2631 {
2632 eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
2633 sal_Int32 nWidthHmm = o3tl::convert(pFramePos->maRect.mnWidth, o3tl::Length::pt, o3tl::Length::mm100);
2634 sal_Int32 nHeightHmm = o3tl::convert(pFramePos->maRect.mnHeight, o3tl::Length::pt, o3tl::Length::mm100);
2635 RelativeSize aRelSize( CalcRelativeFromHmmX( nWidthHmm ), CalcRelativeFromHmmY( nHeightHmm ) );
2636 aLegendProp.SetProperty( EXC_CHPROP_RELATIVESIZE, aRelSize );
2637 }
2638 else
2639 {
2640 // automatic size: determine entry direction from flags
2642 cssc::ChartLegendExpansion_HIGH, cssc::ChartLegendExpansion_WIDE );
2643 }
2644 }
2645 aLegendProp.SetProperty( EXC_CHPROP_ANCHORPOSITION, eApiPos );
2646 aLegendProp.SetProperty( EXC_CHPROP_EXPANSION, eApiExpand );
2647 }
2648 return xLegend;
2649}
2650
2651XclImpChDropBar::XclImpChDropBar( sal_uInt16 nDropBar ) :
2652 mnDropBar( nDropBar ),
2653 mnBarDist( 0 )
2654{
2655}
2656
2658{
2659 mnBarDist = rStrm.ReaduInt16();
2660}
2661
2662void XclImpChDropBar::Convert( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const
2663{
2665 switch( mnDropBar )
2666 {
2667 case EXC_CHDROPBAR_UP: eObjType = EXC_CHOBJTYPE_WHITEDROPBAR; break;
2668 case EXC_CHDROPBAR_DOWN: eObjType = EXC_CHOBJTYPE_BLACKDROPBAR; break;
2669 }
2670 ConvertFrameBase( rRoot, rPropSet, eObjType );
2671}
2672
2674 XclImpChRoot( rRoot ),
2675 maType( rRoot ),
2676 maTypeInfo( maType.GetTypeInfo() )
2677{
2678 // Initialize unused format indexes set. At this time, all formats are unused.
2679 for( sal_uInt16 nFormatIdx = 0; nFormatIdx <= EXC_CHSERIES_MAXSERIES; ++nFormatIdx )
2680 maUnusedFormats.insert( maUnusedFormats.end(), nFormatIdx );
2681}
2682
2684{
2685 rStrm.Ignore( 16 );
2686 maData.mnFlags = rStrm.ReaduInt16();
2687 maData.mnGroupIdx = rStrm.ReaduInt16();
2688}
2689
2691{
2692 switch( rStrm.GetRecId() )
2693 {
2694 case EXC_ID_CHCHART3D:
2695 mxChart3d = std::make_shared<XclImpChChart3d>();
2696 mxChart3d->ReadChChart3d( rStrm );
2697 break;
2698 case EXC_ID_CHLEGEND:
2699 mxLegend = std::make_shared<XclImpChLegend>( GetChRoot() );
2700 mxLegend->ReadRecordGroup( rStrm );
2701 break;
2704 break;
2705 case EXC_ID_CHDROPBAR:
2707 break;
2708 case EXC_ID_CHCHARTLINE:
2710 break;
2713 break;
2714 default:
2716 }
2717}
2718
2720{
2721 // check and set valid chart type
2722 bool bStockChart =
2723 (maType.GetRecId() == EXC_ID_CHLINE) && // must be a line chart
2724 !mxChart3d && // must be a 2d chart
2725 m_ChartLines.find(EXC_CHCHARTLINE_HILO) != m_ChartLines.end() && // must contain hi-lo lines
2726 (maSeries.size() == static_cast<XclImpChSeriesVec::size_type>(HasDropBars() ? 4 : 3)); // correct series count
2727 maType.Finalize( bStockChart );
2728
2729 // extended type info
2730 maTypeInfo.Set( maType.GetTypeInfo(), static_cast< bool >(mxChart3d), false );
2731
2732 // reverse series order for some unstacked 2D chart types
2734 ::std::reverse( maSeries.begin(), maSeries.end() );
2735
2736 // update chart type group format, may depend on chart type finalized above
2737 if( mxGroupFmt )
2738 mxGroupFmt->UpdateGroupFormat( maTypeInfo );
2739}
2740
2742{
2743 if( xSeries )
2744 maSeries.push_back( xSeries );
2745 // store first inserted series separately, series order may be reversed later
2746 if( !mxFirstSeries )
2747 mxFirstSeries = xSeries;
2748}
2749
2750void XclImpChTypeGroup::SetUsedFormatIndex( sal_uInt16 nFormatIdx )
2751{
2752 maUnusedFormats.erase( nFormatIdx );
2753}
2754
2756{
2757 OSL_ENSURE( !maUnusedFormats.empty(), "XclImpChTypeGroup::PopUnusedFormatIndex - no more format indexes available" );
2758 sal_uInt16 nFormatIdx = maUnusedFormats.empty() ? 0 : *maUnusedFormats.begin();
2759 SetUsedFormatIndex( nFormatIdx );
2760 return nFormatIdx;
2761}
2762
2764{
2766 ((maTypeInfo.meVarPointMode == EXC_CHVARPOINT_MULTI) || // multiple series allowed
2767 ((maTypeInfo.meVarPointMode == EXC_CHVARPOINT_SINGLE) && // or exactly 1 series?
2768 (maSeries.size() == 1)));
2769}
2770
2772{
2773 // existence of connector lines (only in stacked bar charts)
2775 return false;
2776 XclImpChLineFormatMap::const_iterator aConLine = m_ChartLines.find(EXC_CHCHARTLINE_CONNECT);
2777 return (aConLine != m_ChartLines.end() && aConLine->second.HasLine());
2778}
2779
2781{
2782 // no automatic title for series with trendlines or error bars
2783 // pie charts always show an automatic title, even if more series exist
2784 return (mxFirstSeries && !mxFirstSeries->HasChildSeries() && (maTypeInfo.mbSingleSeriesVis || (maSeries.size() == 1))) ?
2785 mxFirstSeries->GetTitle() : OUString();
2786}
2787
2789{
2790 if( mxChart3d )
2791 mxChart3d->Convert( rPropSet, Is3dWallChart() );
2792}
2793
2794Reference< XCoordinateSystem > XclImpChTypeGroup::CreateCoordSystem() const
2795{
2797}
2798
2799Reference< XChartType > XclImpChTypeGroup::CreateChartType( Reference< XDiagram > const & xDiagram, sal_Int32 nApiAxesSetIdx ) const
2800{
2801 OSL_ENSURE( IsValidGroup(), "XclImpChTypeGroup::CreateChartType - type group without series" );
2802
2803 // create the chart type object
2804 Reference< XChartType > xChartType = maType.CreateChartType( xDiagram, Is3dChart() );
2805
2806 // bar chart connector lines
2807 if( HasConnectorLines() )
2808 {
2809 ScfPropertySet aDiaProp( xDiagram );
2810 aDiaProp.SetBoolProperty( EXC_CHPROP_CONNECTBARS, true );
2811 }
2812
2813 /* Stock chart needs special processing. Create one 'big' series with
2814 data sequences of different roles. */
2816 CreateStockSeries( xChartType, nApiAxesSetIdx );
2817 else
2818 CreateDataSeries( xChartType, nApiAxesSetIdx );
2819
2820 return xChartType;
2821}
2822
2823Reference< XLabeledDataSequence > XclImpChTypeGroup::CreateCategSequence() const
2824{
2825 Reference< XLabeledDataSequence > xLabeledSeq;
2826 // create category sequence from first visible series
2827 if( mxFirstSeries )
2828 xLabeledSeq = mxFirstSeries->CreateCategSequence( EXC_CHPROP_ROLE_CATEG );
2829 return xLabeledSeq;
2830}
2831
2833{
2834 if (m_DropBars.find(EXC_CHDROPBAR_UP) == m_DropBars.end())
2835 {
2836 unique_ptr<XclImpChDropBar> p(new XclImpChDropBar(EXC_CHDROPBAR_UP));
2837 p->ReadRecordGroup(rStrm);
2838 m_DropBars.insert(std::make_pair(EXC_CHDROPBAR_UP, std::move(p)));
2839 }
2840 else if (m_DropBars.find(EXC_CHDROPBAR_DOWN) == m_DropBars.end())
2841 {
2842 unique_ptr<XclImpChDropBar> p(new XclImpChDropBar(EXC_CHDROPBAR_DOWN));
2843 p->ReadRecordGroup(rStrm);
2844 m_DropBars.insert(std::make_pair(EXC_CHDROPBAR_DOWN, std::move(p)));
2845 }
2846}
2847
2849{
2850 sal_uInt16 nLineId = rStrm.ReaduInt16();
2851 if( (rStrm.GetNextRecId() == EXC_ID_CHLINEFORMAT) && rStrm.StartNextRecord() )
2852 {
2853 XclImpChLineFormat aLineFmt;
2854 aLineFmt.ReadChLineFormat( rStrm );
2855 m_ChartLines[ nLineId ] = aLineFmt;
2856 }
2857}
2858
2860{
2861 // global series and data point format
2862 XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
2863 xDataFmt->ReadRecordGroup( rStrm );
2864 const XclChDataPointPos& rPos = xDataFmt->GetPointPos();
2865 if( (rPos.mnSeriesIdx == 0) && (rPos.mnPointIdx == 0) &&
2866 (xDataFmt->GetFormatIdx() == EXC_CHDATAFORMAT_DEFAULT) )
2867 mxGroupFmt = xDataFmt;
2868}
2869
2870void XclImpChTypeGroup::InsertDataSeries( Reference< XChartType > const & xChartType,
2871 Reference< XDataSeries > const & xSeries, sal_Int32 nApiAxesSetIdx ) const
2872{
2873 Reference< XDataSeriesContainer > xSeriesCont( xChartType, UNO_QUERY );
2874 if( !(xSeriesCont.is() && xSeries.is()) )
2875 return;
2876
2877 // series stacking mode
2878 cssc2::StackingDirection eStacking = cssc2::StackingDirection_NO_STACKING;
2879 // stacked overrides deep-3d
2880 if( maType.IsStacked() || maType.IsPercent() )
2881 eStacking = cssc2::StackingDirection_Y_STACKING;
2882 else if( Is3dDeepChart() )
2883 eStacking = cssc2::StackingDirection_Z_STACKING;
2884
2885 // additional series properties
2886 ScfPropertySet aSeriesProp( xSeries );
2887 aSeriesProp.SetProperty( EXC_CHPROP_STACKINGDIR, eStacking );
2888 aSeriesProp.SetProperty( EXC_CHPROP_ATTAXISINDEX, nApiAxesSetIdx );
2889
2890 // insert series into container
2891 try
2892 {
2893 xSeriesCont->addDataSeries( xSeries );
2894 }
2895 catch( Exception& )
2896 {
2897 OSL_FAIL( "XclImpChTypeGroup::InsertDataSeries - cannot add data series" );
2898 }
2899}
2900
2901void XclImpChTypeGroup::CreateDataSeries( Reference< XChartType > const & xChartType, sal_Int32 nApiAxesSetIdx ) const
2902{
2903 bool bSpline = false;
2904 for (auto const& elem : maSeries)
2905 {
2906 Reference< XDataSeries > xDataSeries = elem->CreateDataSeries();
2907 InsertDataSeries( xChartType, xDataSeries, nApiAxesSetIdx );
2908 bSpline |= elem->HasSpline();
2909 }
2910 // spline - TODO: set at single series (#i66858#)
2912 {
2913 ScfPropertySet aTypeProp( xChartType );
2914 aTypeProp.SetProperty( EXC_CHPROP_CURVESTYLE, css::chart2::CurveStyle_CUBIC_SPLINES );
2915 }
2916}
2917
2918void XclImpChTypeGroup::CreateStockSeries( Reference< XChartType > const & xChartType, sal_Int32 nApiAxesSetIdx ) const
2919{
2920 // create the data series object
2921 Reference< XDataSeries > xDataSeries( ScfApiHelper::CreateInstance( SERVICE_CHART2_DATASERIES ), UNO_QUERY );
2922 Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
2923 if( !xDataSink.is() )
2924 return;
2925
2926 // create a list of data sequences from all series
2927 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
2928 OSL_ENSURE( maSeries.size() >= 3, "XclImpChTypeGroup::CreateChartType - missing stock series" );
2929 int nRoleIdx = (maSeries.size() == 3) ? 1 : 0;
2930 for( const auto& rxSeries : maSeries )
2931 {
2932 // create a data sequence with a specific role
2933 OUString aRole;
2934 switch( nRoleIdx )
2935 {
2936 case 0: aRole = EXC_CHPROP_ROLE_OPENVALUES; break;
2937 case 1: aRole = EXC_CHPROP_ROLE_HIGHVALUES; break;
2938 case 2: aRole = EXC_CHPROP_ROLE_LOWVALUES; break;
2939 case 3: aRole = EXC_CHPROP_ROLE_CLOSEVALUES; break;
2940 }
2941 Reference< XLabeledDataSequence > xDataSeq = rxSeries->CreateValueSequence( aRole );
2942 if( xDataSeq.is() )
2943 aLabeledSeqVec.push_back( xDataSeq );
2944 ++nRoleIdx;
2945 if (nRoleIdx >= 4)
2946 break;
2947 }
2948
2949 // attach labeled data sequences to series and insert series into chart type
2950 xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
2951
2952 // formatting of special stock chart elements
2953 ScfPropertySet aTypeProp( xChartType );
2956 aTypeProp.SetBoolProperty( EXC_CHPROP_SHOWHIGHLOW, true );
2957 // hi-lo line format
2958 XclImpChLineFormatMap::const_iterator aHiLoLine = m_ChartLines.find( EXC_CHCHARTLINE_HILO );
2959 if (aHiLoLine != m_ChartLines.end())
2960 {
2961 ScfPropertySet aSeriesProp( xDataSeries );
2962 aHiLoLine->second.Convert( GetChRoot(), aSeriesProp, EXC_CHOBJTYPE_HILOLINE );
2963 }
2964 // white dropbar format
2965 XclImpChDropBarMap::const_iterator itr = m_DropBars.find(EXC_CHDROPBAR_UP);
2966 Reference<XPropertySet> xWhitePropSet;
2967 if (itr != m_DropBars.end() && aTypeProp.GetProperty(xWhitePropSet, EXC_CHPROP_WHITEDAY))
2968 {
2969 ScfPropertySet aBarProp( xWhitePropSet );
2970 itr->second->Convert(GetChRoot(), aBarProp);
2971 }
2972 // black dropbar format
2973 itr = m_DropBars.find(EXC_CHDROPBAR_DOWN);
2974 Reference<XPropertySet> xBlackPropSet;
2975 if (itr != m_DropBars.end() && aTypeProp.GetProperty(xBlackPropSet, EXC_CHPROP_BLACKDAY))
2976 {
2977 ScfPropertySet aBarProp( xBlackPropSet );
2978 itr->second->Convert(GetChRoot(), aBarProp);
2979 }
2980
2981 // insert the series into the chart type object
2982 InsertDataSeries( xChartType, xDataSeries, nApiAxesSetIdx );
2983}
2984
2985// Axes =======================================================================
2986
2988 XclImpChRoot( rRoot )
2989{
2990}
2991
2993{
2994 maLabelData.mnCross = rStrm.ReaduInt16();
2995 maLabelData.mnLabelFreq = rStrm.ReaduInt16();
2996 maLabelData.mnTickFreq = rStrm.ReaduInt16();
2997 maLabelData.mnFlags = rStrm.ReaduInt16();
2998}
2999
3001{
3002 maDateData.mnMinDate = rStrm.ReaduInt16();
3003 maDateData.mnMaxDate = rStrm.ReaduInt16();
3004 maDateData.mnMajorStep = rStrm.ReaduInt16();
3005 maDateData.mnMajorUnit = rStrm.ReaduInt16();
3006 maDateData.mnMinorStep = rStrm.ReaduInt16();
3007 maDateData.mnMinorUnit = rStrm.ReaduInt16();
3008 maDateData.mnBaseUnit = rStrm.ReaduInt16();
3009 maDateData.mnCross = rStrm.ReaduInt16();
3010 maDateData.mnFlags = rStrm.ReaduInt16();
3011}
3012
3013void XclImpChLabelRange::Convert( ScfPropertySet& rPropSet, ScaleData& rScaleData, bool bMirrorOrient ) const
3014{
3015 // automatic axis type detection
3016 rScaleData.AutoDateAxis = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTODATE );
3017
3018 // the flag EXC_CHDATERANGE_DATEAXIS specifies whether this is a date axis
3020 {
3021 /* Chart2 requires axis type CATEGORY for automatic category/date axis
3022 (even if it is a date axis currently). */
3023 rScaleData.AxisType = rScaleData.AutoDateAxis ? cssc2::AxisType::CATEGORY : cssc2::AxisType::DATE;
3024 rScaleData.Scaling = css::chart2::LinearScaling::create( comphelper::getProcessComponentContext() );
3025 /* Min/max values depend on base time unit, they specify the number of
3026 days, months, or years starting from null date. */
3027 lclConvertTimeValue( GetRoot(), rScaleData.Minimum, maDateData.mnMinDate, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMIN ), maDateData.mnBaseUnit );
3028 lclConvertTimeValue( GetRoot(), rScaleData.Maximum, maDateData.mnMaxDate, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAX ), maDateData.mnBaseUnit );
3029 // increment
3030 cssc::TimeIncrement& rTimeIncrement = rScaleData.TimeIncrement;
3031 lclConvertTimeInterval( rTimeIncrement.MajorTimeInterval, maDateData.mnMajorStep, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAJOR ), maDateData.mnMajorUnit );
3032 lclConvertTimeInterval( rTimeIncrement.MinorTimeInterval, maDateData.mnMinorStep, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMINOR ), maDateData.mnMinorUnit );
3033 // base unit
3035 rTimeIncrement.TimeResolution.clear();
3036 else
3037 rTimeIncrement.TimeResolution <<= lclGetApiTimeUnit( maDateData.mnBaseUnit );
3038 }
3039 else
3040 {
3041 // do not overlap text unless all labels are visible
3043 // do not break text into several lines unless all labels are visible
3045 // do not stagger labels in two lines
3046 rPropSet.SetProperty( EXC_CHPROP_ARRANGEORDER, cssc::ChartAxisArrangeOrderType_SIDE_BY_SIDE );
3047 }
3048
3049 // reverse order
3050 bool bReverse = ::get_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_REVERSE ) != bMirrorOrient;
3051 rScaleData.Orientation = bReverse ? cssc2::AxisOrientation_REVERSE : cssc2::AxisOrientation_MATHEMATICAL;
3052
3053 //TODO #i58731# show n-th category
3054}
3055
3056void XclImpChLabelRange::ConvertAxisPosition( ScfPropertySet& rPropSet, bool b3dChart ) const
3057{
3058 /* Crossing mode (max-cross flag overrides other crossing settings). Excel
3059 does not move the Y axis in 3D charts, regardless of actual settings.
3060 But: the Y axis has to be moved to "end", if the X axis is mirrored,
3061 to keep it at the left end of the chart. */
3063 cssc::ChartAxisPosition eAxisPos = bMaxCross ? cssc::ChartAxisPosition_END : cssc::ChartAxisPosition_VALUE;
3064 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERPOSITION, eAxisPos );
3065
3066 // crossing position (depending on axis type text/date)
3068 {
3070 /* Crossing position value depends on base time unit, it specifies the
3071 number of days, months, or years from null date. Note that Excel
3072 2007/2010 write broken BIFF8 files, they always stores the number
3073 of days regardless of the base time unit (and they are reading it
3074 the same way, thus wrongly displaying files written by Excel
3075 97-2003). This filter sticks to the correct behaviour of Excel
3076 97-2003. */
3077 double fCrossingPos = bAutoCross ? 1.0 : lclGetSerialDay( GetRoot(), maDateData.mnCross, maDateData.mnBaseUnit );
3078 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
3079 }
3080 else
3081 {
3082 double fCrossingPos = b3dChart ? 1.0 : maLabelData.mnCross;
3083 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
3084 }
3085}
3086
3088 XclImpChRoot( rRoot )
3089{
3090}
3091
3093{
3099 maData.mnFlags = rStrm.ReaduInt16();
3100}
3101
3102void XclImpChValueRange::Convert( ScaleData& rScaleData, bool bMirrorOrient ) const
3103{
3104 // scaling algorithm
3105 const bool bLogScale = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE );
3106 if( bLogScale )
3107 rScaleData.Scaling = css::chart2::LogarithmicScaling::create( comphelper::getProcessComponentContext() );
3108 else
3109 rScaleData.Scaling = css::chart2::LinearScaling::create( comphelper::getProcessComponentContext() );
3110
3111 // min/max
3112 lclSetExpValueOrClearAny( rScaleData.Minimum, maData.mfMin, bLogScale, ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMIN ) );
3113 lclSetExpValueOrClearAny( rScaleData.Maximum, maData.mfMax, bLogScale, ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAX ) );
3114
3115 // increment
3118 // major increment
3119 IncrementData& rIncrementData = rScaleData.IncrementData;
3120 lclSetValueOrClearAny( rIncrementData.Distance, maData.mfMajorStep, bAutoMajor );
3121 // minor increment
3122 Sequence< SubIncrement >& rSubIncrementSeq = rIncrementData.SubIncrements;
3123 rSubIncrementSeq.realloc( 1 );
3124 Any& rIntervalCount = rSubIncrementSeq.getArray()[ 0 ].IntervalCount;
3125 rIntervalCount.clear();
3126 if( bLogScale )
3127 {
3128 if( !bAutoMinor )
3129 rIntervalCount <<= sal_Int32( 9 );
3130 }
3131 else if( !bAutoMajor && !bAutoMinor && (0.0 < maData.mfMinorStep) && (maData.mfMinorStep <= maData.mfMajorStep) )
3132 {
3133 double fCount = maData.mfMajorStep / maData.mfMinorStep + 0.5;
3134 if( (1.0 <= fCount) && (fCount < 1001.0) )
3135 rIntervalCount <<= static_cast< sal_Int32 >( fCount );
3136 }
3137 else if( bAutoMinor )
3138 {
3139 // tdf#114168 If minor unit is not set then set interval to 5, as MS Excel do.
3140 rIntervalCount <<= static_cast< sal_Int32 >( 5 );
3141 }
3142
3143 // reverse order
3144 bool bReverse = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_REVERSE ) != bMirrorOrient;
3145 rScaleData.Orientation = bReverse ? cssc2::AxisOrientation_REVERSE : cssc2::AxisOrientation_MATHEMATICAL;
3146}
3147
3149{
3153
3154 // crossing mode (max-cross flag overrides other crossing settings)
3155 cssc::ChartAxisPosition eAxisPos = bMaxCross ? cssc::ChartAxisPosition_END : cssc::ChartAxisPosition_VALUE;
3156 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERPOSITION, eAxisPos );
3157
3158 // crossing position
3159 double fCrossingPos = bAutoCross ? 0.0 : maData.mfCross;
3160 if( bLogScale ) fCrossingPos = pow( 10.0, fCrossingPos );
3161 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
3162}
3163
3164namespace {
3165
3166sal_Int32 lclGetApiTickmarks( sal_uInt8 nXclTickPos )
3167{
3168 using namespace ::com::sun::star::chart2::TickmarkStyle;
3169 sal_Int32 nApiTickmarks = css::chart2::TickmarkStyle::NONE;
3170 ::set_flag( nApiTickmarks, INNER, ::get_flag( nXclTickPos, EXC_CHTICK_INSIDE ) );
3171 ::set_flag( nApiTickmarks, OUTER, ::get_flag( nXclTickPos, EXC_CHTICK_OUTSIDE ) );
3172 return nApiTickmarks;
3173}
3174
3175cssc::ChartAxisLabelPosition lclGetApiLabelPosition( sal_Int8 nXclLabelPos )
3176{
3177 using namespace ::com::sun::star::chart;
3178 switch( nXclLabelPos )
3179 {
3180 case EXC_CHTICK_LOW: return ChartAxisLabelPosition_OUTSIDE_START;
3181 case EXC_CHTICK_HIGH: return ChartAxisLabelPosition_OUTSIDE_END;
3182 case EXC_CHTICK_NEXT: return ChartAxisLabelPosition_NEAR_AXIS;
3183 }
3184 return ChartAxisLabelPosition_NEAR_AXIS;
3185}
3186
3187} // namespace
3188
3190 XclImpChRoot( rRoot )
3191{
3192}
3193
3195{
3196 maData.mnMajor = rStrm.ReaduInt8();
3197 maData.mnMinor = rStrm.ReaduInt8();
3198 maData.mnLabelPos = rStrm.ReaduInt8();
3199 maData.mnBackMode = rStrm.ReaduInt8();
3200 rStrm.Ignore( 16 );
3201 Color aColor;
3202 rStrm >> aColor;
3204 maData.mnFlags = rStrm.ReaduInt16();
3205
3206 if( GetBiff() == EXC_BIFF8 )
3207 {
3208 // BIFF8: index into palette used instead of RGB data
3209 maData.maTextComplexColor.setColor(GetPalette().GetColor(rStrm.ReaduInt16()));
3210 // rotation
3211 maData.mnRotation = rStrm.ReaduInt16();
3212 }
3213 else
3214 {
3215 // BIFF2-BIFF7: get rotation from text orientation
3216 sal_uInt8 nOrient = ::extract_value< sal_uInt8 >( maData.mnFlags, 2, 3 );
3218 }
3219}
3220
3222{
3224}
3225
3227{
3228 /* n#720443: Ignore auto-rotation if there is a suggested rotation.
3229 * Better fix would be to improve our axis auto rotation algorithm.
3230 */
3232 return maData.mnRotation;
3234}
3235
3237{
3238 rPropSet.SetProperty( EXC_CHPROP_MAJORTICKS, lclGetApiTickmarks( maData.mnMajor ) );
3239 rPropSet.SetProperty( EXC_CHPROP_MINORTICKS, lclGetApiTickmarks( maData.mnMinor ) );
3240 rPropSet.SetProperty( EXC_CHPROP_LABELPOSITION, lclGetApiLabelPosition( maData.mnLabelPos ) );
3241 rPropSet.SetProperty( EXC_CHPROP_MARKPOSITION, cssc::ChartAxisMarkPosition_AT_AXIS );
3242}
3243
3244XclImpChAxis::XclImpChAxis( const XclImpChRoot& rRoot, sal_uInt16 nAxisType ) :
3245 XclImpChRoot( rRoot ),
3246 mnNumFmtIdx( EXC_FORMAT_NOTFOUND )
3247{
3248 maData.mnType = nAxisType;
3249}
3250
3252{
3253 maData.mnType = rStrm.ReaduInt16();
3254}
3255
3257{
3258 switch( rStrm.GetRecId() )
3259 {
3261 mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
3262 mxLabelRange->ReadChLabelRange( rStrm );
3263 break;
3264 case EXC_ID_CHDATERANGE:
3265 if( !mxLabelRange )
3266 mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
3267 mxLabelRange->ReadChDateRange( rStrm );
3268 break;
3270 mxValueRange = std::make_shared<XclImpChValueRange>( GetChRoot() );
3271 mxValueRange->ReadChValueRange( rStrm );
3272 break;
3273 case EXC_ID_CHFORMAT:
3274 mnNumFmtIdx = rStrm.ReaduInt16();
3275 break;
3276 case EXC_ID_CHTICK:
3277 mxTick = std::make_shared<XclImpChTick>( GetChRoot() );
3278 mxTick->ReadChTick( rStrm );
3279 break;
3280 case EXC_ID_CHFONT:
3281 mxFont = std::make_shared<XclImpChFont>();
3282 mxFont->ReadChFont( rStrm );
3283 break;
3284 case EXC_ID_CHAXISLINE:
3286 break;
3287 }
3288}
3289
3291{
3292 // add default scaling, needed e.g. to adjust rotation direction of pie and radar charts
3293 if( !mxLabelRange )
3294 mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
3295 if( !mxValueRange )
3296 mxValueRange = std::make_shared<XclImpChValueRange>( GetChRoot() );
3297 // remove invisible grid lines completely
3298 if( mxMajorGrid && !mxMajorGrid->HasLine() )
3299 mxMajorGrid.clear();
3300 if( mxMinorGrid && !mxMinorGrid->HasLine() )
3301 mxMinorGrid.clear();
3302 // default tick settings different in OOChart and Excel
3303 if( !mxTick )
3304 mxTick = std::make_shared<XclImpChTick>( GetChRoot() );
3305 // #i4140# different default axis line color
3306 if( !mxAxisLine )
3307 {
3308 XclChLineFormat aLineFmt;
3309 // set "show axis" flag, default if line format record is missing
3311 mxAxisLine = new XclImpChLineFormat( aLineFmt );
3312 }
3313 // add wall/floor frame for 3d charts
3314 if( !mxWallFrame )
3316}
3317
3319{
3320 return mxFont ? mxFont->GetFontIndex() : EXC_FONT_NOTFOUND;
3321}
3322
3324{
3325 return mxTick ? mxTick->GetFontColor() : GetFontAutoColor();
3326}
3327
3329{
3330 return mxTick ? mxTick->GetRotation() : EXC_CHART_AUTOROTATION;
3331}
3332
3333Reference< XAxis > XclImpChAxis::CreateAxis( const XclImpChTypeGroup& rTypeGroup, const XclImpChAxis* pCrossingAxis ) const
3334{
3335 // create the axis object (always)
3336 Reference< XAxis > xAxis( ScfApiHelper::CreateInstance( SERVICE_CHART2_AXIS ), UNO_QUERY );
3337 if( xAxis.is() )
3338 {
3339 ScfPropertySet aAxisProp( xAxis );
3340 // #i58688# axis enabled
3341 aAxisProp.SetBoolProperty( EXC_CHPROP_SHOW, !mxAxisLine || mxAxisLine->IsShowAxis() );
3342
3343 // axis line properties
3344 if( mxAxisLine )
3345 mxAxisLine->Convert( GetChRoot(), aAxisProp, EXC_CHOBJTYPE_AXISLINE );
3346 // axis ticks properties
3347 if( mxTick )
3348 mxTick->Convert( aAxisProp );
3349
3350 // axis caption text --------------------------------------------------
3351
3352 // radar charts disable their category labels via chart type, not via axis
3353 bool bHasLabels = (!mxTick || mxTick->HasLabels()) &&
3354 ((GetAxisType() != EXC_CHAXIS_X) || rTypeGroup.HasCategoryLabels());
3355 aAxisProp.SetBoolProperty( EXC_CHPROP_DISPLAYLABELS, bHasLabels );
3356 if( bHasLabels )
3357 {
3358 // font settings from CHFONT record or from default text
3359 if( mxFont )
3360 ConvertFontBase( GetChRoot(), aAxisProp );
3361 else if( const XclImpChText* pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_AXISLABEL ) )
3362 pDefText->ConvertFont( aAxisProp );
3363 // label text rotation
3364 ConvertRotationBase( aAxisProp, true );
3365 // number format
3366 bool bLinkNumberFmtToSource = true;
3368 {
3369 sal_uInt32 nScNumFmt = GetNumFmtBuffer().GetScFormat( mnNumFmtIdx );
3370 if( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND )
3371 {
3372 aAxisProp.SetProperty( EXC_CHPROP_NUMBERFORMAT, static_cast< sal_Int32 >( nScNumFmt ) );
3373 bLinkNumberFmtToSource = false;
3374 }
3375 }
3376
3377 aAxisProp.SetProperty( EXC_CHPROP_NUMBERFORMAT_LINKSRC, bLinkNumberFmtToSource );
3378 }
3379
3380 // axis scaling and increment -----------------------------------------
3381
3382 const XclChExtTypeInfo& rTypeInfo = rTypeGroup.GetTypeInfo();
3383 ScaleData aScaleData = xAxis->getScaleData();
3384 // set axis type
3385 switch( GetAxisType() )
3386 {
3387 case EXC_CHAXIS_X:
3388 if( rTypeInfo.mbCategoryAxis )
3389 {
3390 aScaleData.AxisType = cssc2::AxisType::CATEGORY;
3391 aScaleData.Categories = rTypeGroup.CreateCategSequence();
3392 }
3393 else
3394 aScaleData.AxisType = cssc2::AxisType::REALNUMBER;
3395 break;
3396 case EXC_CHAXIS_Y:
3397 aScaleData.AxisType = rTypeGroup.IsPercent() ?
3398 cssc2::AxisType::PERCENT : cssc2::AxisType::REALNUMBER;
3399 break;
3400 case EXC_CHAXIS_Z:
3401 aScaleData.AxisType = cssc2::AxisType::SERIES;
3402 break;
3403 }
3404 // axis scaling settings, dependent on axis type
3405 switch( aScaleData.AxisType )
3406 {
3407 case cssc2::AxisType::CATEGORY:
3408 case cssc2::AxisType::SERIES:
3409 // #i71684# radar charts have reversed rotation direction
3410 if (mxLabelRange)
3411 mxLabelRange->Convert( aAxisProp, aScaleData, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR );
3412 else
3413 SAL_WARN("sc.filter", "missing LabelRange");
3414 break;
3415 case cssc2::AxisType::REALNUMBER:
3417 // #i85167# pie/donut charts have reversed rotation direction (at Y axis!)
3418 if (mxValueRange)
3419 mxValueRange->Convert( aScaleData, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE );
3420 else
3421 SAL_WARN("sc.filter", "missing ValueRange");
3422 break;
3423 default:
3424 OSL_FAIL( "XclImpChAxis::CreateAxis - unknown axis type" );
3425 }
3426
3427 /* Do not set a value to the Origin member anymore (will be done via
3428 new axis properties 'CrossoverPosition' and 'CrossoverValue'). */
3429 aScaleData.Origin.clear();
3430
3431 // write back
3432 xAxis->setScaleData( aScaleData );
3433
3434 // grid ---------------------------------------------------------------
3435
3436 // main grid
3437 ScfPropertySet aGridProp( xAxis->getGridProperties() );
3438 aGridProp.SetBoolProperty( EXC_CHPROP_SHOW, static_cast<bool>(mxMajorGrid) );
3439 if( mxMajorGrid )
3440 mxMajorGrid->Convert( GetChRoot(), aGridProp, EXC_CHOBJTYPE_GRIDLINE );
3441 // sub grid
3442 Sequence< Reference< XPropertySet > > aSubGridPropSeq = xAxis->getSubGridProperties();
3443 if( aSubGridPropSeq.hasElements() )
3444 {
3445 ScfPropertySet aSubGridProp( aSubGridPropSeq[ 0 ] );
3446 aSubGridProp.SetBoolProperty( EXC_CHPROP_SHOW, static_cast<bool>(mxMinorGrid) );
3447 if( mxMinorGrid )
3448 mxMinorGrid->Convert( GetChRoot(), aSubGridProp, EXC_CHOBJTYPE_GRIDLINE );
3449 }
3450
3451 // position of crossing axis ------------------------------------------
3452
3453 if( pCrossingAxis )
3454 pCrossingAxis->ConvertAxisPosition( aAxisProp, rTypeGroup );
3455 }
3456 return xAxis;
3457}
3458
3460{
3461 // #i71810# walls and floor in 3D charts use the CHPICFORMAT record for bitmap mode
3462 if( mxWallFrame )
3463 mxWallFrame->Convert( rPropSet, true );
3464}
3465
3467{
3468 if( ((GetAxisType() == EXC_CHAXIS_X) && rTypeGroup.GetTypeInfo().mbCategoryAxis) || (GetAxisType() == EXC_CHAXIS_Z) )
3469 {
3470 if (mxLabelRange)
3471 mxLabelRange->ConvertAxisPosition( rPropSet, rTypeGroup.Is3dChart() );
3472 else
3473 SAL_WARN("sc.filter", "missing LabelRange");
3474 }
3475 else
3476 {
3477 if (mxValueRange)
3478 mxValueRange->ConvertAxisPosition( rPropSet );
3479 else
3480 SAL_WARN("sc.filter", "missing ValueRange");
3481 }
3482}
3483
3485{
3486 XclImpChLineFormatRef* pxLineFmt = nullptr;
3487 bool bWallFrame = false;
3488 switch( rStrm.ReaduInt16() )
3489 {
3490 case EXC_CHAXISLINE_AXISLINE: pxLineFmt = &mxAxisLine; break;
3491 case EXC_CHAXISLINE_MAJORGRID: pxLineFmt = &mxMajorGrid; break;
3492 case EXC_CHAXISLINE_MINORGRID: pxLineFmt = &mxMinorGrid; break;
3493 case EXC_CHAXISLINE_WALLS: bWallFrame = true; break;
3494 }
3495 if( bWallFrame )
3497
3498 bool bLoop = pxLineFmt || bWallFrame;
3499 while( bLoop )
3500 {
3501 sal_uInt16 nRecId = rStrm.GetNextRecId();
3502 bLoop = ((nRecId == EXC_ID_CHLINEFORMAT) ||
3503 (nRecId == EXC_ID_CHAREAFORMAT) ||
3504 (nRecId == EXC_ID_CHESCHERFORMAT))
3505 && rStrm.StartNextRecord();
3506 if( bLoop )
3507 {
3508 if( pxLineFmt && (nRecId == EXC_ID_CHLINEFORMAT) )
3509 {
3510 (*pxLineFmt) = new XclImpChLineFormat();
3511 (*pxLineFmt)->ReadChLineFormat( rStrm );
3512 }
3513 else if( bWallFrame && mxWallFrame )
3514 {
3515 mxWallFrame->ReadSubRecord( rStrm );
3516 }
3517 }
3518 }
3519}
3520
3522{
3523 switch( GetAxisType() )
3524 {
3525 case EXC_CHAXIS_X:
3526 mxWallFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_WALL3D );
3527 break;
3528 case EXC_CHAXIS_Y:
3529 mxWallFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_FLOOR3D );
3530 break;
3531 default:
3532 mxWallFrame.reset();
3533 }
3534}
3535
3536XclImpChAxesSet::XclImpChAxesSet( const XclImpChRoot& rRoot, sal_uInt16 nAxesSetId ) :
3537 XclImpChRoot( rRoot )
3538{
3539 maData.mnAxesSetId = nAxesSetId;
3540}
3541
3543{
3544 maData.mnAxesSetId = rStrm.ReaduInt16();
3545 rStrm >> maData.maRect;
3546}
3547
3549{
3550 switch( rStrm.GetRecId() )
3551 {
3552 case EXC_ID_CHFRAMEPOS:
3553 mxFramePos = std::make_shared<XclImpChFramePos>();
3554 mxFramePos->ReadChFramePos( rStrm );
3555 break;
3556 case EXC_ID_CHAXIS:
3557 ReadChAxis( rStrm );
3558 break;
3559 case EXC_ID_CHTEXT:
3560 ReadChText( rStrm );
3561 break;
3562 case EXC_ID_CHPLOTFRAME:
3564 break;
3565 case EXC_ID_CHTYPEGROUP:
3567 break;
3568 }
3569}
3570
3572{
3573 if( IsValidAxesSet() )
3574 {
3575 // finalize chart type groups, erase empty groups without series
3576 XclImpChTypeGroupMap aValidGroups;
3577 for (auto const& typeGroup : maTypeGroups)
3578 {
3579 XclImpChTypeGroupRef xTypeGroup = typeGroup.second;
3580 xTypeGroup->Finalize();
3581 if( xTypeGroup->IsValidGroup() )
3582 aValidGroups.emplace(typeGroup.first, xTypeGroup);
3583 }
3584 maTypeGroups.swap( aValidGroups );
3585 }
3586
3587 // invalid chart type groups are deleted now, check again with IsValidAxesSet()
3588 if( !IsValidAxesSet() )
3589 return;
3590
3591 // always create missing axis objects
3592 if( !mxXAxis )
3593 mxXAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_X );
3594 if( !mxYAxis )
3595 mxYAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_Y );
3596 if( !mxZAxis && GetFirstTypeGroup()->Is3dDeepChart() )
3597 mxZAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_Z );
3598
3599 // finalize axes
3600 if( mxXAxis ) mxXAxis->Finalize();
3601 if( mxYAxis ) mxYAxis->Finalize();
3602 if( mxZAxis ) mxZAxis->Finalize();
3603
3604 // finalize axis titles
3606 OUString aAutoTitle(ScResId(STR_AXISTITLE));
3607 lclFinalizeTitle( mxXAxisTitle, pDefText, aAutoTitle );
3608 lclFinalizeTitle( mxYAxisTitle, pDefText, aAutoTitle );
3609 lclFinalizeTitle( mxZAxisTitle, pDefText, aAutoTitle );
3610
3611 // #i47745# missing plot frame -> invisible border and area
3612 if( !mxPlotFrame )
3613 mxPlotFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_PLOTFRAME );
3614}
3615
3617{
3618 XclImpChTypeGroupMap::const_iterator itr = maTypeGroups.find(nGroupIdx);
3619 return itr == maTypeGroups.end() ? XclImpChTypeGroupRef() : itr->second;
3620}
3621
3623{
3624 XclImpChTypeGroupRef xTypeGroup;
3625 if( !maTypeGroups.empty() )
3626 xTypeGroup = maTypeGroups.begin()->second;
3627 return xTypeGroup;
3628}
3629
3631{
3632 XclImpChLegendRef xLegend;
3633 for( const auto& rEntry : maTypeGroups )
3634 {
3635 xLegend = rEntry.second->GetLegend();
3636 if (xLegend)
3637 break;
3638 }
3639 return xLegend;
3640}
3641
3643{
3644 return (maTypeGroups.size() == 1) ? maTypeGroups.begin()->second->GetSingleSeriesTitle() : OUString();
3645}
3646
3647void XclImpChAxesSet::Convert( Reference< XDiagram > const & xDiagram ) const
3648{
3649 if( !(IsValidAxesSet() && xDiagram.is()) )
3650 return;
3651
3652 // diagram background formatting
3654 ConvertBackground( xDiagram );
3655
3656 // create the coordinate system, this inserts all chart types and series
3657 Reference< XCoordinateSystem > xCoordSystem = CreateCoordSystem( xDiagram );
3658 if( !xCoordSystem.is() )
3659 return;
3660
3661 // insert coordinate system, if not already done
3662 try
3663 {
3664 Reference< XCoordinateSystemContainer > xCoordSystemCont( xDiagram, UNO_QUERY_THROW );
3665 Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
3666 if( !aCoordSystems.hasElements() )
3667 xCoordSystemCont->addCoordinateSystem( xCoordSystem );
3668 }
3669 catch( Exception& )
3670 {
3671 OSL_FAIL( "XclImpChAxesSet::Convert - cannot insert coordinate system" );
3672 }
3673
3674 // create the axes with grids and axis titles and insert them into the diagram
3675 ConvertAxis( mxXAxis, mxXAxisTitle, xCoordSystem, mxYAxis.get() );
3676 ConvertAxis( mxYAxis, mxYAxisTitle, xCoordSystem, mxXAxis.get() );
3677 ConvertAxis( mxZAxis, mxZAxisTitle, xCoordSystem, nullptr );
3678}
3679
3681{
3682 if( mxXAxisTitle )
3684 if( mxYAxisTitle )
3686 if( mxZAxisTitle )
3688}
3689
3691{
3692 XclImpChAxisRef xAxis = std::make_shared<XclImpChAxis>( GetChRoot() );
3693 xAxis->ReadRecordGroup( rStrm );
3694
3695 switch( xAxis->GetAxisType() )
3696 {
3697 case EXC_CHAXIS_X: mxXAxis = xAxis; break;
3698 case EXC_CHAXIS_Y: mxYAxis = xAxis; break;
3699 case EXC_CHAXIS_Z: mxZAxis = xAxis; break;
3700 }
3701}
3702
3704{
3705 XclImpChTextRef xText = std::make_shared<XclImpChText>( GetChRoot() );
3706 xText->ReadRecordGroup( rStrm );
3707
3708 switch( xText->GetLinkTarget() )
3709 {
3710 case EXC_CHOBJLINK_XAXIS: mxXAxisTitle = xText; break;
3711 case EXC_CHOBJLINK_YAXIS: mxYAxisTitle = xText; break;
3712 case EXC_CHOBJLINK_ZAXIS: mxZAxisTitle = xText; break;
3713 }
3714}
3715
3717{
3718 if( (rStrm.GetNextRecId() == EXC_ID_CHFRAME) && rStrm.StartNextRecord() )
3719 {
3720 mxPlotFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_PLOTFRAME );
3721 mxPlotFrame->ReadRecordGroup( rStrm );
3722 }
3723}
3724
3726{
3727 XclImpChTypeGroupRef xTypeGroup = std::make_shared<XclImpChTypeGroup>( GetChRoot() );
3728 xTypeGroup->ReadRecordGroup( rStrm );
3729 sal_uInt16 nGroupIdx = xTypeGroup->GetGroupIdx();
3730 XclImpChTypeGroupMap::iterator itr = maTypeGroups.lower_bound(nGroupIdx);
3731 if (itr != maTypeGroups.end() && !maTypeGroups.key_comp()(nGroupIdx, itr->first))
3732 // Overwrite the existing element.
3733 itr->second = xTypeGroup;
3734 else
3735 maTypeGroups.insert(
3736 itr, XclImpChTypeGroupMap::value_type(nGroupIdx, xTypeGroup));
3737}
3738
3739Reference< XCoordinateSystem > XclImpChAxesSet::CreateCoordSystem( Reference< XDiagram > const & xDiagram ) const
3740{
3741 Reference< XCoordinateSystem > xCoordSystem;
3742
3743 /* Try to get existing coordinate system. For now, all series from primary
3744 and secondary axes sets are inserted into one coordinate system. Later,
3745 this should be changed to use one coordinate system for each axes set. */
3746 Reference< XCoordinateSystemContainer > xCoordSystemCont( xDiagram, UNO_QUERY );
3747 if( xCoordSystemCont.is() )
3748 {
3749 Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
3750 OSL_ENSURE( aCoordSystems.getLength() <= 1, "XclImpChAxesSet::CreateCoordSystem - too many existing coordinate systems" );
3751 if( aCoordSystems.hasElements() )
3752 xCoordSystem = aCoordSystems[ 0 ];
3753 }
3754
3755 // create the coordinate system according to the first chart type
3756 if( !xCoordSystem.is() )
3757 {
3759 if( xTypeGroup )
3760 {
3761 xCoordSystem = xTypeGroup->CreateCoordSystem();
3762 // convert 3d chart settings
3763 ScfPropertySet aDiaProp( xDiagram );
3764 xTypeGroup->ConvertChart3d( aDiaProp );
3765 }
3766 }
3767
3768 /* Create XChartType objects for all chart type groups. Each group will
3769 add its series to the data provider attached to the chart document. */
3770 Reference< XChartTypeContainer > xChartTypeCont( xCoordSystem, UNO_QUERY );
3771 if( xChartTypeCont.is() )
3772 {
3773 sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
3774 for( const auto& rEntry : maTypeGroups )
3775 {
3776 try
3777 {
3778 Reference< XChartType > xChartType = rEntry.second->CreateChartType( xDiagram, nApiAxesSetIdx );
3779 if( xChartType.is() )
3780 xChartTypeCont->addChartType( xChartType );
3781 }
3782 catch( Exception& )
3783 {
3784 OSL_FAIL( "XclImpChAxesSet::CreateCoordSystem - cannot add chart type" );
3785 }
3786 }
3787 }
3788
3789 return xCoordSystem;
3790}
3791
3793 XclImpChAxisRef const & xChAxis, XclImpChTextRef const & xChAxisTitle,
3794 Reference< XCoordinateSystem > const & xCoordSystem, const XclImpChAxis* pCrossingAxis ) const
3795{
3796 if( !xChAxis )
3797 return;
3798
3799 // create and attach the axis object
3800 Reference< XAxis > xAxis = CreateAxis( *xChAxis, pCrossingAxis );
3801 if( !xAxis.is() )
3802 return;
3803
3804 // create and attach the axis title
3805 if( xChAxisTitle ) try
3806 {
3807 Reference< XTitled > xTitled( xAxis, UNO_QUERY_THROW );
3808 Reference< XTitle > xTitle( xChAxisTitle->CreateTitle(), UNO_SET_THROW );
3809 xTitled->setTitleObject( xTitle );
3810 }
3811 catch( Exception& )
3812 {
3813 OSL_FAIL( "XclImpChAxesSet::ConvertAxis - cannot set axis title" );
3814 }
3815
3816 // insert axis into coordinate system
3817 try
3818 {
3819 sal_Int32 nApiAxisDim = xChAxis->GetApiAxisDimension();
3820 sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
3821 xCoordSystem->setAxisByDimension( nApiAxisDim, xAxis, nApiAxesSetIdx );
3822 }
3823 catch( Exception& )
3824 {
3825 OSL_FAIL( "XclImpChAxesSet::ConvertAxis - cannot set axis" );
3826 }
3827}
3828
3829Reference< XAxis > XclImpChAxesSet::CreateAxis( const XclImpChAxis& rChAxis, const XclImpChAxis* pCrossingAxis ) const
3830{
3831 Reference< XAxis > xAxis;
3832 if( const XclImpChTypeGroup* pTypeGroup = GetFirstTypeGroup().get() )
3833 xAxis = rChAxis.CreateAxis( *pTypeGroup, pCrossingAxis );
3834 return xAxis;
3835}
3836
3837void XclImpChAxesSet::ConvertBackground( Reference< XDiagram > const & xDiagram ) const
3838{
3840 if( xTypeGroup && xTypeGroup->Is3dWallChart() )
3841 {
3842 // wall/floor formatting (3D charts)
3843 if( mxXAxis )
3844 {
3845 ScfPropertySet aWallProp( xDiagram->getWall() );
3846 mxXAxis->ConvertWall( aWallProp );
3847 }
3848 if( mxYAxis )
3849 {
3850 ScfPropertySet aFloorProp( xDiagram->getFloor() );
3851 mxYAxis->ConvertWall( aFloorProp );
3852 }
3853 }
3854 else if( mxPlotFrame )
3855 {
3856 // diagram background formatting
3857 ScfPropertySet aWallProp( xDiagram->getWall() );
3858 mxPlotFrame->Convert( aWallProp );
3859 }
3860}
3861
3862// The chart object ===========================================================
3863
3865 XclImpChRoot( rRoot, *this )
3866{
3867 mxPrimAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_PRIMARY );
3868 mxSecnAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_SECONDARY );
3869}
3870
3872{
3873}
3874
3876{
3877 // coordinates are stored as 16.16 fixed point
3878 rStrm >> maRect;
3879}
3880
3882{
3883 switch( rStrm.GetRecId() )
3884 {
3885 case EXC_ID_CHFRAME:
3886 mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
3887 mxFrame->ReadRecordGroup( rStrm );
3888 break;
3889 case EXC_ID_CHSERIES:
3891 break;
3894 break;
3897 break;
3898 case EXC_ID_CHAXESSET:
3900 break;
3901 case EXC_ID_CHTEXT:
3902 ReadChText( rStrm );
3903 break;
3904 case EXC_ID_CHEND:
3905 Finalize(); // finalize the entire chart object
3906 break;
3907 }
3908}
3909
3911{
3912 sal_uInt16 nTextId = rStrm.ReaduInt16();
3913 if( (rStrm.GetNextRecId() == EXC_ID_CHTEXT) && rStrm.StartNextRecord() )
3914 {
3915 unique_ptr<XclImpChText> pText(new XclImpChText(GetChRoot()));
3916 pText->ReadRecordGroup(rStrm);
3917 m_DefTexts.insert(std::make_pair(nTextId, std::move(pText)));
3918 }
3919}
3920
3922{
3923 XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
3924 xDataFmt->ReadRecordGroup( rStrm );
3925 if( xDataFmt->GetPointPos().mnSeriesIdx <= EXC_CHSERIES_MAXSERIES )
3926 {
3927 const XclChDataPointPos& rPos = xDataFmt->GetPointPos();
3928 XclImpChDataFormatMap::iterator itr = maDataFmts.lower_bound(rPos);
3929 if (itr == maDataFmts.end() || maDataFmts.key_comp()(rPos, itr->first))
3930 // No element exists for this data point. Insert it.
3931 maDataFmts.insert(
3932 itr, XclImpChDataFormatMap::value_type(rPos, xDataFmt));
3933
3934 /* Do not overwrite existing data format group, Excel always uses the
3935 first data format group occurring in any CHSERIES group. */
3936 }
3937}
3938
3939void XclImpChChart::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
3940{
3941 if( !mxFrame )
3942 mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
3943 mxFrame->UpdateObjFrame( rLineData, rFillData );
3944}
3945
3947{
3948 XclImpChTypeGroupRef xTypeGroup = mxPrimAxesSet->GetTypeGroup( nGroupIdx );
3949 if( !xTypeGroup ) xTypeGroup = mxSecnAxesSet->GetTypeGroup( nGroupIdx );
3950 if( !xTypeGroup ) xTypeGroup = mxPrimAxesSet->GetFirstTypeGroup();
3951 return xTypeGroup;
3952}
3953
3955{
3956 sal_uInt16 nDefTextId = EXC_CHDEFTEXT_GLOBAL;
3957 bool bBiff8 = GetBiff() == EXC_BIFF8;
3958 switch( eTextType )
3959 {
3960 case EXC_CHTEXTTYPE_TITLE: nDefTextId = EXC_CHDEFTEXT_GLOBAL; break;
3961 case EXC_CHTEXTTYPE_LEGEND: nDefTextId = EXC_CHDEFTEXT_GLOBAL; break;
3962 case EXC_CHTEXTTYPE_AXISTITLE: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
3963 case EXC_CHTEXTTYPE_AXISLABEL: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
3964 case EXC_CHTEXTTYPE_DATALABEL: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
3965 }
3966
3967 XclImpChTextMap::const_iterator const itr = m_DefTexts.find(nDefTextId);
3968 return itr == m_DefTexts.end() ? nullptr : itr->second.get();
3969}
3970
3972{
3973 // there is no real automatic mode in BIFF5 charts
3975}
3976
3977void XclImpChChart::Convert( const Reference<XChartDocument>& xChartDoc,
3978 XclImpDffConverter& rDffConv, const OUString& rObjName, const tools::Rectangle& rChartRect ) const
3979{
3980 // initialize conversion (locks the model to suppress any internal updates)
3981 InitConversion( xChartDoc, rChartRect );
3982
3983 // chart frame formatting
3984 if( mxFrame )
3985 {
3986 ScfPropertySet aFrameProp( xChartDoc->getPageBackground() );
3987 mxFrame->Convert( aFrameProp );
3988 }
3989
3990 // chart title
3991 if( mxTitle ) try
3992 {
3993 Reference< XTitled > xTitled( xChartDoc, UNO_QUERY_THROW );
3994 Reference< XTitle > xTitle( mxTitle->CreateTitle(), UNO_SET_THROW );
3995 xTitled->setTitleObject( xTitle );
3996 }
3997 catch( Exception& )
3998 {
3999 }
4000
4001 /* Create the diagram object and attach it to the chart document. Currently,
4002 one diagram is used to carry all coordinate systems and data series. */
4003 Reference< XDiagram > xDiagram = CreateDiagram();
4004 xChartDoc->setFirstDiagram( xDiagram );
4005
4006 // coordinate systems and chart types, convert axis settings
4007 mxPrimAxesSet->Convert( xDiagram );
4008 mxSecnAxesSet->Convert( xDiagram );
4009
4010 // legend
4011 if( xDiagram.is() && mxLegend )
4012 xDiagram->setLegend( mxLegend->CreateLegend() );
4013
4014 /* Following all conversions needing the old Chart1 API that involves full
4015 initialization of the chart view. */
4016 Reference< cssc::XChartDocument > xChart1Doc( xChartDoc, UNO_QUERY );
4017 if( xChart1Doc.is() )
4018 {
4019 Reference< cssc::XDiagram > xDiagram1 = xChart1Doc->getDiagram();
4020
4021 /* Set the 'IncludeHiddenCells' property via the old API as only this
4022 ensures that the data provider and all created sequences get this
4023 flag correctly. */
4024 ScfPropertySet aDiaProp( xDiagram1 );
4025 bool bShowVisCells = ::get_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY );
4026 aDiaProp.SetBoolProperty( EXC_CHPROP_INCLUDEHIDDENCELLS, !bShowVisCells );
4027
4028 // plot area position and size (there is no real automatic mode in BIFF5 charts)
4029 XclImpChFramePosRef xPlotAreaPos = mxPrimAxesSet->GetPlotAreaFramePos();
4030 if( IsManualPlotArea() && xPlotAreaPos ) try
4031 {
4032 const XclChFramePos& rFramePos = xPlotAreaPos->GetFramePosData();
4033 if( (rFramePos.mnTLMode == EXC_CHFRAMEPOS_PARENT) && (rFramePos.mnBRMode == EXC_CHFRAMEPOS_PARENT) )
4034 {
4035 Reference< cssc::XDiagramPositioning > xPositioning( xDiagram1, UNO_QUERY_THROW );
4036 css::awt::Rectangle aDiagramRect = CalcHmmFromChartRect( rFramePos.maRect );
4037 // for pie charts, always set inner plot area size to exclude the data labels as Excel does
4038 const XclImpChTypeGroup* pFirstTypeGroup = mxPrimAxesSet->GetFirstTypeGroup().get();
4039 if( pFirstTypeGroup && (pFirstTypeGroup->GetTypeInfo().meTypeCateg == EXC_CHTYPECATEG_PIE) )
4040 xPositioning->setDiagramPositionExcludingAxes( aDiagramRect );
4041 else if( pFirstTypeGroup && pFirstTypeGroup->Is3dChart() )
4042 xPositioning->setDiagramPositionIncludingAxesAndAxisTitles( aDiagramRect );
4043 else
4044 xPositioning->setDiagramPositionIncludingAxes( aDiagramRect );
4045 }
4046 }
4047 catch( Exception& )
4048 {
4049 }
4050
4051 // positions of all title objects
4052 if( mxTitle )
4053 mxTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_TITLE ) );
4054 mxPrimAxesSet->ConvertTitlePositions();
4055 mxSecnAxesSet->ConvertTitlePositions();
4056 }
4057
4058 // unlock the model
4059 FinishConversion( rDffConv );
4060
4061 // start listening to this chart
4062 ScDocument& rDoc = GetRoot().GetDoc();
4063 ScChartListenerCollection* pChartCollection = rDoc.GetChartListenerCollection();
4064 if(!pChartCollection)
4065 return;
4066
4067 std::vector< ScTokenRef > aRefTokens;
4068 for( const auto& rxSeries : maSeries )
4069 rxSeries->FillAllSourceLinks( aRefTokens );
4070 if( !aRefTokens.empty() )
4071 {
4072 ::std::unique_ptr< ScChartListener > xListener( new ScChartListener( rObjName, rDoc, std::move(aRefTokens) ) );
4073 xListener->SetUsed( true );
4074 xListener->StartListeningTo();
4075 pChartCollection->insert( xListener.release() );
4076 }
4077}
4078
4080{
4081 sal_uInt16 nNewSeriesIdx = static_cast< sal_uInt16 >( maSeries.size() );
4082 XclImpChSeriesRef xSeries = std::make_shared<XclImpChSeries>( GetChRoot(), nNewSeriesIdx );
4083 xSeries->ReadRecordGroup( rStrm );
4084 maSeries.push_back( xSeries );
4085}
4086
4088{
4089 maProps.mnFlags = rStrm.ReaduInt16();
4090 maProps.mnEmptyMode = rStrm.ReaduInt8();
4091}
4092
4094{
4095 XclImpChAxesSetRef xAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_NONE );
4096 xAxesSet->ReadRecordGroup( rStrm );
4097 switch( xAxesSet->GetAxesSetId() )
4098 {
4099 case EXC_CHAXESSET_PRIMARY: mxPrimAxesSet = xAxesSet; break;
4100 case EXC_CHAXESSET_SECONDARY: mxSecnAxesSet = xAxesSet; break;
4101 }
4102}
4103
4105{
4106 XclImpChTextRef xText = std::make_shared<XclImpChText>( GetChRoot() );
4107 xText->ReadRecordGroup( rStrm );
4108 switch( xText->GetLinkTarget() )
4109 {
4111 mxTitle = xText;
4112 break;
4113 case EXC_CHOBJLINK_DATA:
4114 {
4115 sal_uInt16 nSeriesIdx = xText->GetPointPos().mnSeriesIdx;
4116 if( nSeriesIdx < maSeries.size() )
4117 maSeries[ nSeriesIdx ]->SetDataLabel( xText );
4118 }
4119 break;
4120 }
4121}
4122
4124{
4125 // finalize series (must be done first)
4127 // #i49218# legend may be attached to primary or secondary axes set
4128 mxLegend = mxPrimAxesSet->GetLegend();
4129 if( !mxLegend )
4130 mxLegend = mxSecnAxesSet->GetLegend();
4131 if( mxLegend )
4132 mxLegend->Finalize();
4133 // axes sets, updates chart type group default formats -> must be called before FinalizeDataFormats()
4134 mxPrimAxesSet->Finalize();
4135 mxSecnAxesSet->Finalize();
4136 // formatting of all series
4138 // #i47745# missing frame -> invisible border and area
4139 if( !mxFrame )
4140 mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
4141 // chart title
4142 FinalizeTitle();
4143}
4144
4146{
4147 for( const XclImpChSeriesRef& xSeries : maSeries )
4148 {
4149 if( xSeries->HasParentSeries() )
4150 {
4151 /* Process child series (trend lines and error bars). Data of
4152 child series will be set at the connected parent series. */
4153 if( xSeries->GetParentIdx() < maSeries.size() )
4154 maSeries[ xSeries->GetParentIdx() ]->AddChildSeries( *xSeries );
4155 }
4156 else
4157 {
4158 // insert the series into the related chart type group
4159 if( XclImpChTypeGroup* pTypeGroup = GetTypeGroup( xSeries->GetGroupIdx() ).get() )
4160 pTypeGroup->AddSeries( xSeries );
4161 }
4162 }
4163}
4164
4166{
4167 /* #i51639# (part 1): CHDATAFORMAT groups are part of CHSERIES groups.
4168 Each CHDATAFORMAT group specifies the series and data point it is
4169 assigned to. This makes it possible to have a data format that is
4170 related to another series, e.g. a CHDATAFORMAT group for series 2 is
4171 part of a CHSERIES group that describes series 1. Therefore the chart
4172 itself has collected all CHDATAFORMAT groups to be able to store data
4173 format groups for series that have not been imported at that time. This
4174 loop finally assigns these groups to the related series. */
4175 for( const auto& [rPos, rDataFmt] : maDataFmts )
4176 {
4177 sal_uInt16 nSeriesIdx = rPos.mnSeriesIdx;
4178 if( nSeriesIdx < maSeries.size() )
4179 maSeries[ nSeriesIdx ]->SetDataFormat( rDataFmt );
4180 }
4181
4182 /* #i51639# (part 2): Finalize data formats of all series. This adds for
4183 example missing CHDATAFORMAT groups for entire series that are needed
4184 for automatic colors of lines and areas. */
4185 for( auto& rxSeries : maSeries )
4186 rxSeries->FinalizeDataFormats();
4187}
4188
4190{
4191 // special handling for auto-generated title
4192 OUString aAutoTitle;
4193 if( !mxTitle || (!mxTitle->IsDeleted() && !mxTitle->HasString()) )
4194 {
4195 // automatic title from first series name (if there are no series on secondary axes set)
4196 if( !mxSecnAxesSet->IsValidAxesSet() )
4197 aAutoTitle = mxPrimAxesSet->GetSingleSeriesTitle();
4198 if( mxTitle || (!aAutoTitle.isEmpty()) )
4199 {
4200 if( !mxTitle )
4201 mxTitle = std::make_shared<XclImpChText>( GetChRoot() );
4202 if( aAutoTitle.isEmpty() )
4203 aAutoTitle = ScResId(STR_CHARTTITLE);
4204 }
4205 }
4206
4207 // will reset mxTitle, if it does not contain a string and no auto title exists
4208 lclFinalizeTitle( mxTitle, GetDefaultText( EXC_CHTEXTTYPE_TITLE ), aAutoTitle );
4209}
4210
4211Reference< XDiagram > XclImpChChart::CreateDiagram() const
4212{
4213 // create a diagram object
4214 Reference< XDiagram > xDiagram( ScfApiHelper::CreateInstance( SERVICE_CHART2_DIAGRAM ), UNO_QUERY );
4215
4216 // convert global chart settings
4217 ScfPropertySet aDiaProp( xDiagram );
4218
4219 // treatment of missing values
4220 using namespace cssc::MissingValueTreatment;
4221 sal_Int32 nMissingValues = LEAVE_GAP;
4222 switch( maProps.mnEmptyMode )
4223 {
4224 case EXC_CHPROPS_EMPTY_SKIP: nMissingValues = LEAVE_GAP; break;
4225 case EXC_CHPROPS_EMPTY_ZERO: nMissingValues = USE_ZERO; break;
4226 case EXC_CHPROPS_EMPTY_INTERPOLATE: nMissingValues = CONTINUE; break;
4227 }
4228 aDiaProp.SetProperty( EXC_CHPROP_MISSINGVALUETREATMENT, nMissingValues );
4229
4230 return xDiagram;
4231}
4232
4234 XclImpDrawing( rRoot, bOwnTab ), // sheet charts may contain OLE objects
4235 mnScTab( rRoot.GetCurrScTab() ),
4236 mbOwnTab( bOwnTab )
4237{
4238}
4239
4241 const Reference< XModel >& rxModel, const tools::Rectangle& rChartRect )
4242{
4243 maChartRect = rChartRect; // needed in CalcAnchorRect() callback
4244
4245 SdrModel* pSdrModel = nullptr;
4246 SdrPage* pSdrPage = nullptr;
4247 if( mbOwnTab )
4248 {
4249 // chart sheet: insert all shapes into the sheet, not into the chart object
4250 pSdrModel = GetDoc().GetDrawLayer();
4251 pSdrPage = GetSdrPage( mnScTab );
4252 }
4253 else
4254 {
4255 // embedded chart object: insert all shapes into the chart
4256 try
4257 {
4258 Reference< XDrawPageSupplier > xDrawPageSupp( rxModel, UNO_QUERY_THROW );
4259 Reference< XDrawPage > xDrawPage( xDrawPageSupp->getDrawPage(), UNO_SET_THROW );
4260 pSdrPage = ::GetSdrPageFromXDrawPage( xDrawPage );
4261 pSdrModel = pSdrPage ? &pSdrPage->getSdrModelFromSdrPage() : nullptr;
4262 }
4263 catch( Exception& )
4264 {
4265 }
4266 }
4267
4268 if( pSdrModel && pSdrPage )
4269 ImplConvertObjects( rDffConv, *pSdrModel, *pSdrPage );
4270}
4271
4273{
4274 /* In objects with DFF client anchor, the position of the shape is stored
4275 in the cell address components of the client anchor. In old BIFF3-BIFF5
4276 objects, the position is stored in the offset components of the anchor. */
4277 tools::Rectangle aRect(
4278 static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maFirst.mnCol : rAnchor.mnLX ) / EXC_CHART_TOTALUNITS * maChartRect.GetWidth() + 0.5 ),
4279 static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maFirst.mnRow : rAnchor.mnTY ) / EXC_CHART_TOTALUNITS * maChartRect.GetHeight() + 0.5 ),
4280 static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maLast.mnCol : rAnchor.mnRX ) / EXC_CHART_TOTALUNITS * maChartRect.GetWidth() + 0.5 ),
4281 static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maLast.mnRow : rAnchor.mnBY ) / EXC_CHART_TOTALUNITS * maChartRect.GetHeight() + 0.5 ) );
4282 aRect.Normalize();
4283 // move shapes into chart area for sheet charts
4284 if( mbOwnTab )
4285 aRect.Move( maChartRect.Left(), maChartRect.Top() );
4286 return aRect;
4287}
4288
4290{
4291}
4292
4293XclImpChart::XclImpChart( const XclImpRoot& rRoot, bool bOwnTab ) :
4294 XclImpRoot( rRoot ),
4295 mbOwnTab( bOwnTab ),
4296 mbIsPivotChart( false )
4297{
4298}
4299
4301{
4302}
4303
4305{
4306 XclImpPageSettings& rPageSett = GetPageSettings();
4307 XclImpTabViewSettings& rTabViewSett = GetTabViewSettings();
4308
4309 bool bLoop = true;
4310 while( bLoop && rStrm.StartNextRecord() )
4311 {
4312 // page settings - only for charts in entire sheet
4313 if( mbOwnTab ) switch( rStrm.GetRecId() )
4314 {
4316 case EXC_ID_VERPAGEBREAKS: rPageSett.ReadPageBreaks( rStrm ); break;
4317 case EXC_ID_HEADER:
4318 case EXC_ID_FOOTER: rPageSett.ReadHeaderFooter( rStrm ); break;
4319 case EXC_ID_LEFTMARGIN:
4320 case EXC_ID_RIGHTMARGIN:
4321 case EXC_ID_TOPMARGIN:
4322 case EXC_ID_BOTTOMMARGIN: rPageSett.ReadMargin( rStrm ); break;
4323 case EXC_ID_PRINTHEADERS: rPageSett.ReadPrintHeaders( rStrm ); break;
4324 case EXC_ID_PRINTGRIDLINES: rPageSett.ReadPrintGridLines( rStrm ); break;
4325 case EXC_ID_HCENTER:
4326 case EXC_ID_VCENTER: rPageSett.ReadCenter( rStrm ); break;
4327 case EXC_ID_SETUP: rPageSett.ReadSetup( rStrm ); break;
4328 case EXC_ID8_IMGDATA: rPageSett.ReadImgData( rStrm ); break;
4329
4330 case EXC_ID_WINDOW2: rTabViewSett.ReadWindow2( rStrm, true );break;
4331 case EXC_ID_SCL: rTabViewSett.ReadScl( rStrm ); break;
4332
4333 case EXC_ID_SHEETEXT: //0x0862
4334 {
4335 // FIXME: do not need to pass palette, XclImpTabVieSettings is derived from root
4336 XclImpPalette& rPal = GetPalette();
4337 rTabViewSett.ReadTabBgColor( rStrm, rPal);
4338 }
4339 break;
4340
4341 case EXC_ID_CODENAME: ReadCodeName( rStrm, false ); break;
4342 }
4343
4344 // common records
4345 switch( rStrm.GetRecId() )
4346 {
4347 case EXC_ID_EOF: bLoop = false; break;
4348
4349 // #i31882# ignore embedded chart objects
4350 case EXC_ID2_BOF:
4351 case EXC_ID3_BOF:
4352 case EXC_ID4_BOF:
4354
4355 case EXC_ID_CHCHART: ReadChChart( rStrm ); break;
4356
4357 case EXC_ID8_CHPIVOTREF:
4359 mbIsPivotChart = true;
4360 break;
4361
4362 // BIFF specific records
4363 default: switch( GetBiff() )
4364 {
4365 case EXC_BIFF5: switch( rStrm.GetRecId() )
4366 {
4367 case EXC_ID_OBJ: GetChartDrawing().ReadObj( rStrm ); break;
4368 }
4369 break;
4370 case EXC_BIFF8: switch( rStrm.GetRecId() )
4371 {
4373 // #i61786# weird documents: OBJ without MSODRAWING -> read in BIFF5 format
4374 case EXC_ID_OBJ: GetChartDrawing().ReadObj( rStrm ); break;
4375 }
4376 break;
4377 default:;
4378 }
4379 }
4380 }
4381}
4382
4383void XclImpChart::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
4384{
4385 if( !mxChartData )
4386 mxChartData = std::make_shared<XclImpChChart>( GetRoot() );
4387 mxChartData->UpdateObjFrame( rLineData, rFillData );
4388}
4389
4391{
4392 return
4394 (mxChartDrawing ? mxChartDrawing->GetProgressSize() : 0);
4395}
4396
4397void XclImpChart::Convert( Reference< XModel > const & xModel, XclImpDffConverter& rDffConv, const OUString& rObjName, const tools::Rectangle& rChartRect ) const
4398{
4399 Reference< XChartDocument > xChartDoc( xModel, UNO_QUERY );
4400 if( xChartDoc.is() )
4401 {
4402 if( mxChartData )
4403 mxChartData->Convert( xChartDoc, rDffConv, rObjName, rChartRect );
4404 if( mxChartDrawing )
4405 mxChartDrawing->ConvertObjects( rDffConv, xModel, rChartRect );
4406 }
4407}
4408
4410{
4411 if( !mxChartDrawing )
4412 mxChartDrawing = std::make_shared<XclImpChartDrawing>( GetRoot(), mbOwnTab );
4413 return *mxChartDrawing;
4414}
4415
4417{
4418 mxChartData = std::make_shared<XclImpChChart>( GetRoot() );
4419 mxChartData->ReadRecordGroup( rStrm );
4420}
4421
4422/* 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:2233
SC_DLLPUBLIC ScDrawLayer * GetDrawLayer()
Definition: document.hxx:1084
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:1360
void ReadCh3dDataFormat(XclImpStream &rStrm)
Reads the CH3DDATAFORMAT record (3D bar properties).
Definition: xichart.cxx:1354
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:1375
XclImpChAttachedLabel(const XclImpChRoot &rRoot)
Definition: xichart.cxx:1369
XclImpChTextRef CreateDataLabel(const XclImpChText *pParent) const
Creates a CHTEXT group for the label.
Definition: xichart.cxx:1380
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:3837
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:3647
XclImpChAxesSet(const XclImpChRoot &rRoot, sal_uInt16 nAxesSetId)
Definition: xichart.cxx:3536
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:3642
void ConvertTitlePositions() const
Converts the manual positions of all axis titles.
Definition: xichart.cxx:3680
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:3739
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:3571
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:3548
void ReadChPlotFrame(XclImpStream &rStrm)
Reads the CHPLOTFRAME record group containing diagram area formatting.
Definition: xichart.cxx:3716
void ReadChAxis(XclImpStream &rStrm)
Reads a CHAXIS record group containing a single axis.
Definition: xichart.cxx:3690
void ReadChText(XclImpStream &rStrm)
Reads a CHTEXT record group containing an axis title.
Definition: xichart.cxx:3703