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 <config_feature_desktop.h>
21
22 #include "SeriesPlotterContainer.hxx"
23
24 #include <ChartView.hxx>
25 #include <chartview/DrawModelWrapper.hxx>
26 #include <Diagram.hxx>
27 #include <ChartType.hxx>
28 #include <DataSeries.hxx>
29 #include <NumberFormatterWrapper.hxx>
30 #include <VDiagram.hxx>
31 #include "VTitle.hxx"
32 #include "VButton.hxx"
33 #include <ShapeFactory.hxx>
34 #include <BaseCoordinateSystem.hxx>
35 #include <VCoordinateSystem.hxx>
36 #include <VSeriesPlotter.hxx>
37 #include <CommonConverters.hxx>
38 #include <TitleHelper.hxx>
39 #include <Legend.hxx>
40 #include <LegendHelper.hxx>
41 #include "VLegend.hxx"
42 #include <PropertyMapper.hxx>
43 #include <ChartModel.hxx>
44 #include <ChartTypeHelper.hxx>
45 #include <ScaleAutomatism.hxx>
46 #include <ObjectIdentifier.hxx>
47 #include <DiagramHelper.hxx>
48 #include <RelativePositionHelper.hxx>
49 #include <servicenames.hxx>
50 #include <Axis.hxx>
51 #include <AxisHelper.hxx>
52 #include "AxisUsage.hxx"
53 #include <AxisIndexDefines.hxx>
54 #include <BaseGFXHelper.hxx>
55 #include <DataSeriesHelper.hxx>
56 #include <DateHelper.hxx>
57 #include <ExplicitCategoriesProvider.hxx>
58 #include <defines.hxx>
59 #include <comphelper/dumpxmltostring.hxx>
60 #include <unonames.hxx>
61 #include <editeng/frmdiritem.hxx>
62 #include <editeng/eeitem.hxx>
63 #include <tools/globname.hxx>
64 #include <comphelper/fileformat.h>
65 #include <comphelper/propertyvalue.hxx>
66 #include <comphelper/scopeguard.hxx>
67 #include <comphelper/servicehelper.hxx>
68 #include <cppuhelper/supportsservice.hxx>
69 #include <rtl/math.hxx>
70 #include <unotools/streamwrap.hxx>
71 #include <svx/svdpage.hxx>
72 #include <svx/unopage.hxx>
73 #include <utility>
74 #include <vcl/svapp.hxx>
75 #include <osl/mutex.hxx>
76 #include <svx/unofill.hxx>
77 #include <drawinglayer/XShapeDumper.hxx>
78 #include <sfx2/objsh.hxx>
79
80 #include <time.h>
81
82 #include <com/sun/star/awt/Point.hpp>
83 #include <com/sun/star/chart/ChartAxisPosition.hpp>
84 #include <com/sun/star/chart/TimeUnit.hpp>
85 #include <com/sun/star/chart2/AxisType.hpp>
86 #include <com/sun/star/chart2/StackingDirection.hpp>
87 #include <com/sun/star/chart2/RelativePosition.hpp>
88 #include <com/sun/star/chart2/RelativeSize.hpp>
89 #include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
90 #include <com/sun/star/chart2/data/PivotTableFieldEntry.hpp>
91 #include <com/sun/star/drawing/GraphicExportFilter.hpp>
92 #include <com/sun/star/embed/Aspects.hpp>
93 #include <com/sun/star/io/XSeekable.hpp>
94 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
95 #include <com/sun/star/util/XRefreshable.hpp>
96 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
97 #include <com/sun/star/text/XText.hpp>
98 #include <com/sun/star/text/XTextDocument.hpp>
99 #include <com/sun/star/text/WritingMode2.hpp>
100 #include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp>
101 #include <com/sun/star/view/XSelectionSupplier.hpp>
102 #include <svl/itempool.hxx>
103 #include <svl/ctloptions.hxx>
104 #include <comphelper/classids.hxx>
105 #include <servicenames_charttypes.hxx>
106
107
108 #include <rtl/ustring.hxx>
109
110 #include <comphelper/diagnose_ex.hxx>
111 #include <tools/stream.hxx>
112
113 #include <memory>
114 #include <libxml/xmlwriter.h>
115
116 namespace chart {
117
118 using namespace ::com::sun::star;
119 using ::com::sun::star::uno::Reference;
120 using ::com::sun::star::uno::Sequence;
121 using ::com::sun::star::uno::Any;
122
123 struct CreateShapeParam2D
124 {
125 css::awt::Rectangle maRemainingSpace;
126
127 std::shared_ptr<SeriesPlotterContainer> mpSeriesPlotterContainer;
128
129 std::shared_ptr<VTitle> mpVTitleX;
130 std::shared_ptr<VTitle> mpVTitleY;
131 std::shared_ptr<VTitle> mpVTitleZ;
132
133 std::shared_ptr<VTitle> mpVTitleSecondX;
134 std::shared_ptr<VTitle> mpVTitleSecondY;
135
136 rtl::Reference<SvxShapeRect> mxMarkHandles;
137 rtl::Reference<SvxShapeRect> mxPlotAreaWithAxes;
138
139 rtl::Reference<SvxShapeGroup> mxDiagramWithAxesShapes;
140
141 bool mbAutoPosTitleX;
142 bool mbAutoPosTitleY;
143 bool mbAutoPosTitleZ;
144
145 bool mbAutoPosSecondTitleX;
146 bool mbAutoPosSecondTitleY;
147
148 bool mbUseFixedInnerSize;
149
CreateShapeParam2Dchart::CreateShapeParam2D150 CreateShapeParam2D() :
151 mbAutoPosTitleX(true),
152 mbAutoPosTitleY(true),
153 mbAutoPosTitleZ(true),
154 mbAutoPosSecondTitleX(true),
155 mbAutoPosSecondTitleY(true),
156 mbUseFixedInnerSize(false) {}
157 };
158
159
160
ChartView(uno::Reference<uno::XComponentContext> xContext,ChartModel & rModel)161 ChartView::ChartView(
162 uno::Reference<uno::XComponentContext> xContext,
163 ChartModel& rModel)
164 : m_xCC(std::move(xContext))
165 , mrChartModel(rModel)
166 , m_bViewDirty(true)
167 , m_bInViewUpdate(false)
168 , m_bViewUpdatePending(false)
169 , m_bRefreshAddIn(true)
170 , m_aPageResolution(1000,1000)
171 , m_bPointsWereSkipped(false)
172 , m_nScaleXNumerator(1)
173 , m_nScaleXDenominator(1)
174 , m_nScaleYNumerator(1)
175 , m_nScaleYDenominator(1)
176 , m_bSdrViewIsInEditMode(false)
177 , m_aResultingDiagramRectangleExcludingAxes(0,0,0,0)
178 {
179 init();
180 }
181
init()182 void ChartView::init()
183 {
184 if( !m_pDrawModelWrapper )
185 {
186 SolarMutexGuard aSolarGuard;
187 m_pDrawModelWrapper = std::make_shared< DrawModelWrapper >();
188 m_xShapeFactory = m_pDrawModelWrapper->getShapeFactory();
189 m_xDrawPage = m_pDrawModelWrapper->getMainDrawPage();
190 StartListening( m_pDrawModelWrapper->getSdrModel() );
191 }
192 }
193
initialize(const uno::Sequence<uno::Any> &)194 void SAL_CALL ChartView::initialize( const uno::Sequence< uno::Any >& )
195 {
196 init();
197 }
198
~ChartView()199 ChartView::~ChartView()
200 {
201 maTimeBased.maTimer.Stop();
202 // #i120831#. In ChartView::initialize(), m_xShapeFactory is created from SdrModel::getUnoModel() and indirectly
203 // from SfxBaseModel, it needs call dispose() to make sure SfxBaseModel object is freed correctly.
204 uno::Reference< lang::XComponent > xComp( m_xShapeFactory, uno::UNO_QUERY);
205 if ( xComp.is() )
206 xComp->dispose();
207
208 if( m_pDrawModelWrapper )
209 {
210 SolarMutexGuard aSolarGuard;
211 EndListening( m_pDrawModelWrapper->getSdrModel() );
212 m_pDrawModelWrapper.reset();
213 }
214 m_xDrawPage = nullptr;
215 impl_deleteCoordinateSystems();
216 }
217
impl_deleteCoordinateSystems()218 void ChartView::impl_deleteCoordinateSystems()
219 {
220 //delete all coordinate systems
221 m_aVCooSysList.clear();
222 }
223
224 // datatransfer::XTransferable
225 namespace
226 {
227 constexpr OUString lcl_aGDIMetaFileMIMEType(
228 u"application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\""_ustr );
229 constexpr OUString lcl_aGDIMetaFileMIMETypeHighContrast(
230 u"application/x-openoffice-highcontrast-gdimetafile;windows_formatname=\"GDIMetaFile\""_ustr );
231 } // anonymous namespace
232
getMetaFile(const uno::Reference<io::XOutputStream> & xOutStream,bool bUseHighContrast)233 void ChartView::getMetaFile( const uno::Reference< io::XOutputStream >& xOutStream
234 , bool bUseHighContrast )
235 {
236 if( !m_xDrawPage.is() )
237 return;
238
239 // creating the graphic exporter
240 uno::Reference< drawing::XGraphicExportFilter > xExporter = drawing::GraphicExportFilter::create( m_xCC );
241
242 uno::Sequence< beans::PropertyValue > aFilterData{
243 comphelper::makePropertyValue(u"ExportOnlyBackground"_ustr, false),
244 comphelper::makePropertyValue(u"HighContrast"_ustr, bUseHighContrast),
245 comphelper::makePropertyValue(u"Version"_ustr, sal_Int32(SOFFICE_FILEFORMAT_50)),
246 comphelper::makePropertyValue(u"CurrentPage"_ustr, uno::Reference< uno::XInterface >( static_cast<cppu::OWeakObject*>(m_xDrawPage.get()), uno::UNO_QUERY )),
247 //#i75867# poor quality of ole's alternative view with 3D scenes and zoomfactors besides 100%
248 comphelper::makePropertyValue(u"ScaleXNumerator"_ustr, m_nScaleXNumerator),
249 comphelper::makePropertyValue(u"ScaleXDenominator"_ustr, m_nScaleXDenominator),
250 comphelper::makePropertyValue(u"ScaleYNumerator"_ustr, m_nScaleYNumerator),
251 comphelper::makePropertyValue(u"ScaleYDenominator"_ustr, m_nScaleYDenominator)
252 };
253
254 uno::Sequence< beans::PropertyValue > aProps{
255 comphelper::makePropertyValue(u"FilterName"_ustr, u"SVM"_ustr),
256 comphelper::makePropertyValue(u"OutputStream"_ustr, xOutStream),
257 comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData)
258 };
259
260 xExporter->setSourceDocument( m_xDrawPage );
261 if( xExporter->filter( aProps ) )
262 {
263 xOutStream->flush();
264 xOutStream->closeOutput();
265 uno::Reference< io::XSeekable > xSeekable( xOutStream, uno::UNO_QUERY );
266 if( xSeekable.is() )
267 xSeekable->seek(0);
268 }
269 }
270
getTransferData(const datatransfer::DataFlavor & aFlavor)271 uno::Any SAL_CALL ChartView::getTransferData( const datatransfer::DataFlavor& aFlavor )
272 {
273 bool bHighContrastMetaFile( aFlavor.MimeType == lcl_aGDIMetaFileMIMETypeHighContrast);
274 uno::Any aRet;
275 if( ! (bHighContrastMetaFile || aFlavor.MimeType == lcl_aGDIMetaFileMIMEType) )
276 return aRet;
277
278 update();
279
280 SvMemoryStream aStream( 1024, 1024 );
281 rtl::Reference<utl::OStreamWrapper> pStreamWrapper = new utl::OStreamWrapper( aStream );
282
283 this->getMetaFile( pStreamWrapper, bHighContrastMetaFile );
284
285 pStreamWrapper->seek(0);
286 sal_Int32 nBytesToRead = pStreamWrapper->available();
287 uno::Sequence< sal_Int8 > aSeq( nBytesToRead );
288 pStreamWrapper->readBytes( aSeq, nBytesToRead);
289 aRet <<= aSeq;
290 pStreamWrapper->closeInput();
291
292 return aRet;
293 }
getTransferDataFlavors()294 uno::Sequence< datatransfer::DataFlavor > SAL_CALL ChartView::getTransferDataFlavors()
295 {
296 return
297 {
298 { lcl_aGDIMetaFileMIMEType, u"GDIMetaFile"_ustr, cppu::UnoType<uno::Sequence< sal_Int8 >>::get() },
299 { lcl_aGDIMetaFileMIMETypeHighContrast, u"GDIMetaFile"_ustr, cppu::UnoType<uno::Sequence< sal_Int8 >>::get() }
300 };
301 }
isDataFlavorSupported(const datatransfer::DataFlavor & aFlavor)302 sal_Bool SAL_CALL ChartView::isDataFlavorSupported( const datatransfer::DataFlavor& aFlavor )
303 {
304 return ( aFlavor.MimeType == lcl_aGDIMetaFileMIMEType ||
305 aFlavor.MimeType == lcl_aGDIMetaFileMIMETypeHighContrast );
306 }
307
308 // lang::XServiceInfo
309
getImplementationName()310 OUString SAL_CALL ChartView::getImplementationName()
311 {
312 return CHART_VIEW_SERVICE_IMPLEMENTATION_NAME;
313 }
314
supportsService(const OUString & rServiceName)315 sal_Bool SAL_CALL ChartView::supportsService( const OUString& rServiceName )
316 {
317 return cppu::supportsService(this, rServiceName);
318 }
319
getSupportedServiceNames()320 css::uno::Sequence< OUString > SAL_CALL ChartView::getSupportedServiceNames()
321 {
322 return { CHART_VIEW_SERVICE_NAME };
323 }
324
createTransformationSceneToScreen(const::basegfx::B2IRectangle & rDiagramRectangleWithoutAxes)325 static ::basegfx::B3DHomMatrix createTransformationSceneToScreen(
326 const ::basegfx::B2IRectangle& rDiagramRectangleWithoutAxes )
327 {
328 ::basegfx::B3DHomMatrix aM;
329 aM.scale(double(rDiagramRectangleWithoutAxes.getWidth())/FIXED_SIZE_FOR_3D_CHART_VOLUME
330 , -double(rDiagramRectangleWithoutAxes.getHeight())/FIXED_SIZE_FOR_3D_CHART_VOLUME, 1.0 );
331 aM.translate(double(rDiagramRectangleWithoutAxes.getMinX())
332 , double(rDiagramRectangleWithoutAxes.getMinY()+rDiagramRectangleWithoutAxes.getHeight()-1), 0);
333 return aM;
334 }
335
336 namespace
337 {
338
lcl_IsPieOrDonut(const rtl::Reference<Diagram> & xDiagram)339 bool lcl_IsPieOrDonut( const rtl::Reference< Diagram >& xDiagram )
340 {
341 //special treatment for pie charts
342 //the size is checked after complete creation to get the datalabels into the given space
343
344 //todo: this is just a workaround at the moment for pie and donut labels
345 return xDiagram->isPieOrDonutChart();
346 }
347
lcl_setDefaultWritingMode(const std::shared_ptr<DrawModelWrapper> & pDrawModelWrapper,ChartModel & rModel)348 void lcl_setDefaultWritingMode( const std::shared_ptr< DrawModelWrapper >& pDrawModelWrapper, ChartModel& rModel)
349 {
350 //get writing mode from parent document:
351 if( !SvtCTLOptions::IsCTLFontEnabled() )
352 return;
353
354 try
355 {
356 sal_Int16 nWritingMode=-1;
357 uno::Reference< beans::XPropertySet > xParentProps( rModel.getParent(), uno::UNO_QUERY );
358 uno::Reference< style::XStyleFamiliesSupplier > xStyleFamiliesSupplier( xParentProps, uno::UNO_QUERY );
359 if( xStyleFamiliesSupplier.is() )
360 {
361 uno::Reference< container::XNameAccess > xStylesFamilies( xStyleFamiliesSupplier->getStyleFamilies() );
362 if( xStylesFamilies.is() )
363 {
364 if( !xStylesFamilies->hasByName( u"PageStyles"_ustr ) )
365 {
366 //draw/impress is parent document
367 uno::Reference< lang::XMultiServiceFactory > xFatcory( xParentProps, uno::UNO_QUERY );
368 if( xFatcory.is() )
369 {
370 uno::Reference< beans::XPropertySet > xDrawDefaults( xFatcory->createInstance( u"com.sun.star.drawing.Defaults"_ustr ), uno::UNO_QUERY );
371 if( xDrawDefaults.is() )
372 xDrawDefaults->getPropertyValue( u"WritingMode"_ustr ) >>= nWritingMode;
373 }
374 }
375 else
376 {
377 uno::Reference< container::XNameAccess > xPageStyles( xStylesFamilies->getByName( u"PageStyles"_ustr ), uno::UNO_QUERY );
378 if( xPageStyles.is() )
379 {
380 OUString aPageStyle;
381
382 uno::Reference< text::XTextDocument > xTextDocument( xParentProps, uno::UNO_QUERY );
383 if( xTextDocument.is() )
384 {
385 //writer is parent document
386 //retrieve the current page style from the text cursor property PageStyleName
387
388 uno::Reference< text::XTextEmbeddedObjectsSupplier > xTextEmbeddedObjectsSupplier( xTextDocument, uno::UNO_QUERY );
389 if( xTextEmbeddedObjectsSupplier.is() )
390 {
391 uno::Reference< container::XNameAccess > xEmbeddedObjects( xTextEmbeddedObjectsSupplier->getEmbeddedObjects() );
392 if( xEmbeddedObjects.is() )
393 {
394 uno::Sequence< OUString > aNames( xEmbeddedObjects->getElementNames() );
395
396 sal_Int32 nCount = aNames.getLength();
397 for( sal_Int32 nN=0; nN<nCount; nN++ )
398 {
399 uno::Reference< beans::XPropertySet > xEmbeddedProps( xEmbeddedObjects->getByName( aNames[nN] ), uno::UNO_QUERY );
400 if( xEmbeddedProps.is() )
401 {
402 static OUString aChartCLSID = SvGlobalName( SO3_SCH_CLASSID ).GetHexName();
403 OUString aCLSID;
404 xEmbeddedProps->getPropertyValue( u"CLSID"_ustr ) >>= aCLSID;
405 if( aCLSID == aChartCLSID )
406 {
407 uno::Reference< text::XTextContent > xEmbeddedObject( xEmbeddedProps, uno::UNO_QUERY );
408 if( xEmbeddedObject.is() )
409 {
410 uno::Reference< text::XTextRange > xAnchor( xEmbeddedObject->getAnchor() );
411 if( xAnchor.is() )
412 {
413 uno::Reference< beans::XPropertySet > xAnchorProps( xAnchor, uno::UNO_QUERY );
414 if( xAnchorProps.is() )
415 {
416 xAnchorProps->getPropertyValue( u"WritingMode"_ustr ) >>= nWritingMode;
417 }
418 uno::Reference< text::XText > xText( xAnchor->getText() );
419 if( xText.is() )
420 {
421 uno::Reference< beans::XPropertySet > xTextCursorProps( xText->createTextCursor(), uno::UNO_QUERY );
422 if( xTextCursorProps.is() )
423 xTextCursorProps->getPropertyValue( u"PageStyleName"_ustr ) >>= aPageStyle;
424 }
425 }
426 }
427 break;
428 }
429 }
430 }
431 }
432 }
433 if( aPageStyle.isEmpty() )
434 {
435 uno::Reference< text::XText > xText( xTextDocument->getText() );
436 if( xText.is() )
437 {
438 uno::Reference< beans::XPropertySet > xTextCursorProps( xText->createTextCursor(), uno::UNO_QUERY );
439 if( xTextCursorProps.is() )
440 xTextCursorProps->getPropertyValue( u"PageStyleName"_ustr ) >>= aPageStyle;
441 }
442 }
443 if(aPageStyle.isEmpty())
444 aPageStyle = "Standard";
445 }
446 else
447 {
448 //Calc is parent document
449 Reference< com::sun::star::beans::XPropertySetInfo > xInfo = xParentProps->getPropertySetInfo();
450 if (xInfo->hasPropertyByName(u"PageStyle"_ustr))
451 {
452 xParentProps->getPropertyValue( u"PageStyle"_ustr ) >>= aPageStyle;
453 }
454 if(aPageStyle.isEmpty())
455 aPageStyle = "Default";
456 }
457 if( nWritingMode == -1 || nWritingMode == text::WritingMode2::PAGE )
458 {
459 uno::Reference< beans::XPropertySet > xPageStyle( xPageStyles->getByName( aPageStyle ), uno::UNO_QUERY );
460 Reference< com::sun::star::beans::XPropertySetInfo > xInfo = xPageStyle->getPropertySetInfo();
461 if (xInfo->hasPropertyByName(u"WritingMode"_ustr))
462 {
463 if( xPageStyle.is() )
464 xPageStyle->getPropertyValue( u"WritingMode"_ustr ) >>= nWritingMode;
465 }
466 }
467 }
468 }
469 }
470 }
471 if( nWritingMode != -1 && nWritingMode != text::WritingMode2::PAGE )
472 {
473 if( pDrawModelWrapper )
474 pDrawModelWrapper->GetItemPool().SetUserDefaultItem(SvxFrameDirectionItem(static_cast<SvxFrameDirection>(nWritingMode), EE_PARA_WRITINGDIR) );
475 }
476 }
477 catch( const uno::Exception& )
478 {
479 DBG_UNHANDLED_EXCEPTION("chart2" );
480 }
481 }
482
lcl_getDefaultWritingModeFromPool(const std::shared_ptr<DrawModelWrapper> & pDrawModelWrapper)483 sal_Int16 lcl_getDefaultWritingModeFromPool( const std::shared_ptr<DrawModelWrapper>& pDrawModelWrapper )
484 {
485 sal_Int16 nWritingMode = text::WritingMode2::LR_TB;
486 if(!pDrawModelWrapper)
487 return nWritingMode;
488
489 const SfxPoolItem& rItem = pDrawModelWrapper->GetItemPool().GetUserOrPoolDefaultItem(EE_PARA_WRITINGDIR);
490 nWritingMode
491 = static_cast<sal_Int16>(static_cast<const SvxFrameDirectionItem&>(rItem).GetValue());
492 return nWritingMode;
493 }
494
495 } //end anonymous namespace
496
impl_createDiagramAndContent(const CreateShapeParam2D & rParam,const awt::Size & rPageSize)497 awt::Rectangle ChartView::impl_createDiagramAndContent( const CreateShapeParam2D& rParam, const awt::Size& rPageSize )
498 {
499 //return the used rectangle
500 awt::Rectangle aUsedOuterRect(rParam.maRemainingSpace.X, rParam.maRemainingSpace.Y, 0, 0);
501
502 rtl::Reference< Diagram > xDiagram( mrChartModel.getFirstChartDiagram() );
503 if( !xDiagram.is())
504 return aUsedOuterRect;
505
506 sal_Int32 nDimensionCount = xDiagram->getDimension();
507 if(!nDimensionCount)
508 {
509 //@todo handle mixed dimension
510 nDimensionCount = 2;
511 }
512
513 basegfx::B2IRectangle aAvailableOuterRect = BaseGFXHelper::makeRectangle(rParam.maRemainingSpace);
514
515 const std::vector< std::unique_ptr<VCoordinateSystem> >& rVCooSysList( rParam.mpSeriesPlotterContainer->getCooSysList() );
516 auto& rSeriesPlotterList = rParam.mpSeriesPlotterContainer->getSeriesPlotterList();
517
518 //create VAxis, so they can give necessary information for automatic scaling
519 uno::Reference<util::XNumberFormatsSupplier> const xNumberFormatsSupplier(
520 mrChartModel.getNumberFormatsSupplier());
521
522 for (auto& rpVCooSys : rVCooSysList)
523 {
524 if (nDimensionCount == 3)
525 {
526 CuboidPlanePosition eLeftWallPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardLeftWall( xDiagram ) );
527 CuboidPlanePosition eBackWallPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBackWall( xDiagram ) );
528 CuboidPlanePosition eBottomPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBottom( xDiagram ) );
529 rpVCooSys->set3DWallPositions( eLeftWallPos, eBackWallPos, eBottomPos );
530 }
531 rpVCooSys->createVAxisList(&mrChartModel, rPageSize, rParam.maRemainingSpace,
532 rParam.mbUseFixedInnerSize, rSeriesPlotterList, getComponentContext());
533 }
534
535 // - prepare list of all axis and how they are used
536 Date aNullDate = NumberFormatterWrapper( xNumberFormatsSupplier ).getNullDate();
537 rParam.mpSeriesPlotterContainer->initAxisUsageList(aNullDate);
538 rParam.mpSeriesPlotterContainer->doAutoScaling( mrChartModel );
539 rParam.mpSeriesPlotterContainer->setScalesFromCooSysToPlotter();
540 rParam.mpSeriesPlotterContainer->setNumberFormatsFromAxes();
541
542 //create shapes
543
544 //aspect ratio
545 drawing::Direction3D aPreferredAspectRatio =
546 rParam.mpSeriesPlotterContainer->getPreferredAspectRatio();
547
548 rtl::Reference<SvxShapeGroupAnyD> xSeriesTargetInFrontOfAxis;
549 rtl::Reference<SvxShapeGroupAnyD> xSeriesTargetBehindAxis;
550 VDiagram aVDiagram(xDiagram, aPreferredAspectRatio, nDimensionCount);
551 {//create diagram
552 aVDiagram.init(rParam.mxDiagramWithAxesShapes);
553 aVDiagram.createShapes(
554 awt::Point(rParam.maRemainingSpace.X, rParam.maRemainingSpace.Y),
555 awt::Size(rParam.maRemainingSpace.Width, rParam.maRemainingSpace.Height));
556
557 xSeriesTargetInFrontOfAxis = aVDiagram.getCoordinateRegion();
558 // It is preferable to use full size than minimum for pie charts
559 if (!rParam.mbUseFixedInnerSize)
560 aVDiagram.reduceToMinimumSize();
561 }
562
563 rtl::Reference<SvxShapeGroup> xTextTargetShapes =
564 ShapeFactory::createGroup2D(rParam.mxDiagramWithAxesShapes);
565
566 // - create axis and grids for all coordinate systems
567
568 //init all coordinate systems
569 for (auto& rpVCooSys : rVCooSysList)
570 {
571 rpVCooSys->initPlottingTargets(xSeriesTargetInFrontOfAxis, xTextTargetShapes, xSeriesTargetBehindAxis);
572
573 rpVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
574 createTransformationSceneToScreen( aVDiagram.getCurrentRectangle() ) ));
575
576 rpVCooSys->initVAxisInList();
577 }
578
579 //calculate resulting size respecting axis label layout and fontscaling
580
581 rtl::Reference<SvxShapeGroup> xBoundingShape(rParam.mxDiagramWithAxesShapes);
582 ::basegfx::B2IRectangle aConsumedOuterRect;
583
584 //use first coosys only so far; todo: calculate for more than one coosys if we have more in future
585 //todo: this is just a workaround at the moment for pie and donut labels
586 bool bIsPieOrDonut = lcl_IsPieOrDonut(xDiagram);
587 if( !bIsPieOrDonut && (!rVCooSysList.empty()) )
588 {
589 VCoordinateSystem* pVCooSys = rVCooSysList[0].get();
590 pVCooSys->createMaximumAxesLabels();
591
592 aConsumedOuterRect = ShapeFactory::getRectangleOfShape(*xBoundingShape);
593 ::basegfx::B2IRectangle aNewInnerRect( aVDiagram.getCurrentRectangle() );
594 if (!rParam.mbUseFixedInnerSize)
595 aNewInnerRect = aVDiagram.adjustInnerSize( aConsumedOuterRect );
596
597 pVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
598 createTransformationSceneToScreen( aNewInnerRect ) ));
599
600 //redo autoscaling to get size and text dependent automatic main increment count
601 rParam.mpSeriesPlotterContainer->doAutoScaling( mrChartModel );
602 rParam.mpSeriesPlotterContainer->updateScalesAndIncrementsOnAxes();
603 rParam.mpSeriesPlotterContainer->setScalesFromCooSysToPlotter();
604
605 pVCooSys->createAxesLabels();
606
607 bool bLessSpaceConsumedThanExpected = false;
608 {
609 aConsumedOuterRect = ShapeFactory::getRectangleOfShape(*xBoundingShape);
610 if( aConsumedOuterRect.getMinX() > aAvailableOuterRect.getMinX()
611 || aConsumedOuterRect.getMaxX() < aAvailableOuterRect.getMaxX()
612 || aConsumedOuterRect.getMinY() > aAvailableOuterRect.getMinY()
613 || aConsumedOuterRect.getMinY() < aAvailableOuterRect.getMaxY() )
614 {
615 bLessSpaceConsumedThanExpected = true;
616 }
617 }
618
619 if (bLessSpaceConsumedThanExpected && !rParam.mbUseFixedInnerSize)
620 {
621 aVDiagram.adjustInnerSize( aConsumedOuterRect );
622 pVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
623 createTransformationSceneToScreen( aVDiagram.getCurrentRectangle() ) ));
624
625 // Need to re-adjust again if the labels have changed height because of
626 // text can break. Ideally this shouldn't be needed, but the chart height
627 // isn't readjusted otherwise.
628 pVCooSys->createAxesLabels();
629 aConsumedOuterRect = ShapeFactory::getRectangleOfShape(*xBoundingShape);
630 aVDiagram.adjustInnerSize(aConsumedOuterRect);
631 pVCooSys->setTransformationSceneToScreen(B3DHomMatrixToHomogenMatrix(
632 createTransformationSceneToScreen(aVDiagram.getCurrentRectangle())));
633
634 }
635 pVCooSys->updatePositions();//todo: logically this belongs to the condition above, but it seems also to be necessary to give the axes group shapes the right bounding rects for hit test - probably caused by bug i106183 -> check again if fixed
636 }
637
638 //create axes and grids for the final size
639 for (auto& rpVCooSys : rVCooSysList)
640 {
641 rpVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
642 createTransformationSceneToScreen( aVDiagram.getCurrentRectangle() ) ));
643
644 rpVCooSys->createAxesShapes();
645 rpVCooSys->createGridShapes();
646 }
647
648 // - create data series for all charttypes
649 m_bPointsWereSkipped = false;
650 for( const std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
651 {
652 VSeriesPlotter* pSeriesPlotter = aPlotter.get();
653 rtl::Reference<SvxShapeGroupAnyD> xSeriesTarget;
654 if( pSeriesPlotter->WantToPlotInFrontOfAxisLine() )
655 xSeriesTarget = xSeriesTargetInFrontOfAxis;
656 else
657 {
658 xSeriesTarget = xSeriesTargetBehindAxis;
659 OSL_ENSURE( !bIsPieOrDonut, "not implemented yet! - during a complete recreation this shape is destroyed so no series can be created anymore" );
660 }
661 pSeriesPlotter->initPlotter( xSeriesTarget,xTextTargetShapes,OUString() );
662 pSeriesPlotter->setPageReferenceSize( rPageSize );
663 VCoordinateSystem* pVCooSys = SeriesPlotterContainer::getCooSysForPlotter( rVCooSysList, pSeriesPlotter );
664 if(nDimensionCount==2)
665 pSeriesPlotter->setTransformationSceneToScreen( pVCooSys->getTransformationSceneToScreen() );
666 //better performance for big data
667 {
668 //calculate resolution for coordinate system
669 Sequence<sal_Int32> aCoordinateSystemResolution = pVCooSys->getCoordinateSystemResolution( rPageSize, m_aPageResolution );
670 pSeriesPlotter->setCoordinateSystemResolution( aCoordinateSystemResolution );
671 }
672 // Do not allow to move data labels in case of pie or donut chart, yet!
673 pSeriesPlotter->setPieLabelsAllowToMove(!bIsPieOrDonut);
674 // use the pagesize as remaining space if we have a fixed inner size
675 if( rParam.mbUseFixedInnerSize )
676 aAvailableOuterRect = BaseGFXHelper::makeRectangle(awt::Rectangle(0, 0, rPageSize.Width, rPageSize.Height));
677 // set the available space for data labels to avoid moving out from chart area
678 pSeriesPlotter->setAvailableOuterRect(aAvailableOuterRect);
679 pSeriesPlotter->createShapes();
680 m_bPointsWereSkipped = m_bPointsWereSkipped || pSeriesPlotter->PointsWereSkipped();
681 }
682
683 //recreate all with corrected sizes if requested
684 if( bIsPieOrDonut )
685 {
686 m_bPointsWereSkipped = false;
687
688 aConsumedOuterRect = ShapeFactory::getRectangleOfShape(*xBoundingShape);
689 ::basegfx::B2IRectangle aNewInnerRect( aVDiagram.getCurrentRectangle() );
690 if (!rParam.mbUseFixedInnerSize)
691 aNewInnerRect = aVDiagram.adjustInnerSize( aConsumedOuterRect );
692
693 for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
694 {
695 aPlotter->releaseShapes();
696 }
697
698 //clear and recreate
699 ShapeFactory::removeSubShapes( xSeriesTargetInFrontOfAxis ); //xSeriesTargetBehindAxis is a sub shape of xSeriesTargetInFrontOfAxis and will be removed here
700 xSeriesTargetBehindAxis.clear();
701 ShapeFactory::removeSubShapes( xTextTargetShapes );
702
703 //set new transformation
704 for (auto& rpVCooSys : rVCooSysList)
705 {
706 auto aMatrix = createTransformationSceneToScreen(aNewInnerRect);
707 rpVCooSys->setTransformationSceneToScreen(B3DHomMatrixToHomogenMatrix(aMatrix));
708 }
709
710 // - create data series for all charttypes
711 for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
712 {
713 VCoordinateSystem* pVCooSys = SeriesPlotterContainer::getCooSysForPlotter( rVCooSysList, aPlotter.get() );
714 if(nDimensionCount==2)
715 aPlotter->setTransformationSceneToScreen( pVCooSys->getTransformationSceneToScreen() );
716 // Now we can move data labels in case of pie or donut chart!
717 aPlotter->setPieLabelsAllowToMove(bIsPieOrDonut);
718 aPlotter->createShapes();
719 m_bPointsWereSkipped = m_bPointsWereSkipped || aPlotter->PointsWereSkipped();
720 }
721
722 for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
723 aPlotter->rearrangeLabelToAvoidOverlapIfRequested(rPageSize);
724 }
725
726 if (rParam.mbUseFixedInnerSize)
727 {
728 aUsedOuterRect = awt::Rectangle( aConsumedOuterRect.getMinX(), aConsumedOuterRect.getMinY(), aConsumedOuterRect.getWidth(), aConsumedOuterRect.getHeight() );
729 }
730 else
731 aUsedOuterRect = rParam.maRemainingSpace;
732
733 bool bSnapRectToUsedArea = false;
734 for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
735 {
736 bSnapRectToUsedArea = aPlotter->shouldSnapRectToUsedArea();
737 if(bSnapRectToUsedArea)
738 break;
739 }
740 if(bSnapRectToUsedArea)
741 {
742 if (rParam.mbUseFixedInnerSize)
743 m_aResultingDiagramRectangleExcludingAxes = getRectangleOfObject( u"PlotAreaExcludingAxes"_ustr );
744 else
745 {
746 ::basegfx::B2IRectangle aConsumedInnerRect = aVDiagram.getCurrentRectangle();
747 m_aResultingDiagramRectangleExcludingAxes = BaseGFXHelper::toAwtRectangle(aConsumedInnerRect);
748 }
749 }
750 else
751 {
752 if (rParam.mbUseFixedInnerSize)
753 m_aResultingDiagramRectangleExcludingAxes = rParam.maRemainingSpace;
754 else
755 {
756 ::basegfx::B2IRectangle aConsumedInnerRect = aVDiagram.getCurrentRectangle();
757 m_aResultingDiagramRectangleExcludingAxes = BaseGFXHelper::toAwtRectangle(aConsumedInnerRect);
758 }
759 }
760
761 if (rParam.mxMarkHandles.is())
762 {
763 awt::Point aPos(rParam.maRemainingSpace.X, rParam.maRemainingSpace.Y);
764 awt::Size aSize(rParam.maRemainingSpace.Width, rParam.maRemainingSpace.Height);
765
766 bool bPosSizeExcludeAxesProperty = true;
767 xDiagram->getPropertyValue(u"PosSizeExcludeAxes"_ustr) >>= bPosSizeExcludeAxesProperty;
768 if (rParam.mbUseFixedInnerSize || bPosSizeExcludeAxesProperty)
769 {
770 aPos = awt::Point( m_aResultingDiagramRectangleExcludingAxes.X, m_aResultingDiagramRectangleExcludingAxes.Y );
771 aSize = awt::Size( m_aResultingDiagramRectangleExcludingAxes.Width, m_aResultingDiagramRectangleExcludingAxes.Height );
772 }
773 rParam.mxMarkHandles->setPosition(aPos);
774 rParam.mxMarkHandles->setSize(aSize);
775 }
776
777 return aUsedOuterRect;
778 }
779
getExplicitValuesForAxis(rtl::Reference<Axis> xAxis,ExplicitScaleData & rExplicitScale,ExplicitIncrementData & rExplicitIncrement)780 bool ChartView::getExplicitValuesForAxis(
781 rtl::Reference< Axis > xAxis
782 , ExplicitScaleData& rExplicitScale
783 , ExplicitIncrementData& rExplicitIncrement )
784 {
785 SolarMutexGuard aSolarGuard;
786
787 impl_updateView();
788
789 if(!xAxis.is())
790 return false;
791
792 rtl::Reference< BaseCoordinateSystem > xCooSys = AxisHelper::getCoordinateSystemOfAxis(xAxis, mrChartModel.getFirstChartDiagram() );
793 const VCoordinateSystem* pVCooSys = SeriesPlotterContainer::findInCooSysList(m_aVCooSysList, xCooSys);
794 if(!pVCooSys)
795 return false;
796
797 sal_Int32 nDimensionIndex=-1;
798 sal_Int32 nAxisIndex=-1;
799 if( !AxisHelper::getIndicesForAxis( xAxis, xCooSys, nDimensionIndex, nAxisIndex ) )
800 return false;
801
802 rExplicitScale = pVCooSys->getExplicitScale(nDimensionIndex,nAxisIndex);
803 rExplicitIncrement = pVCooSys->getExplicitIncrement(nDimensionIndex,nAxisIndex);
804 if( !rExplicitScale.m_bShiftedCategoryPosition )
805 return true;
806
807 //remove 'one' from max
808 if( rExplicitScale.AxisType == css::chart2::AxisType::DATE )
809 {
810 Date aMaxDate(rExplicitScale.NullDate); aMaxDate.AddDays(::rtl::math::approxFloor(rExplicitScale.Maximum));
811 //for explicit scales with shifted categories we need one interval more
812 switch( rExplicitScale.TimeResolution )
813 {
814 case css::chart::TimeUnit::DAY:
815 --aMaxDate;
816 break;
817 case css::chart::TimeUnit::MONTH:
818 aMaxDate = DateHelper::GetDateSomeMonthsAway(aMaxDate,-1);
819 break;
820 case css::chart::TimeUnit::YEAR:
821 aMaxDate = DateHelper::GetDateSomeYearsAway(aMaxDate,-1);
822 break;
823 }
824 rExplicitScale.Maximum = aMaxDate - rExplicitScale.NullDate;
825 }
826 else if( rExplicitScale.AxisType == css::chart2::AxisType::CATEGORY )
827 rExplicitScale.Maximum -= 1.0;
828 else if( rExplicitScale.AxisType == css::chart2::AxisType::SERIES )
829 rExplicitScale.Maximum -= 1.0;
830 return true;
831 }
832
getSdrPage()833 SdrPage* ChartView::getSdrPage()
834 {
835 if(m_xDrawPage)
836 return m_xDrawPage->GetSdrPage();
837
838 return nullptr;
839 }
840
getShapeForCID(const OUString & rObjectCID)841 rtl::Reference< SvxShape > ChartView::getShapeForCID( const OUString& rObjectCID )
842 {
843 SolarMutexGuard aSolarGuard;
844 SdrObject* pObj = DrawModelWrapper::getNamedSdrObject( rObjectCID, this->getSdrPage() );
845 if( !pObj )
846 return nullptr;
847
848 uno::Reference< drawing::XShape > xShape = pObj->getUnoShape();
849 rtl::Reference<SvxShape> xShape2 = dynamic_cast<SvxShape*>(xShape.get());
850 assert(xShape2 || !xShape);
851 return xShape2;
852 }
853
getDiagramRectangleExcludingAxes()854 awt::Rectangle ChartView::getDiagramRectangleExcludingAxes()
855 {
856 impl_updateView();
857 return m_aResultingDiagramRectangleExcludingAxes;
858 }
859
getRectangleOfObject(const OUString & rObjectCID,bool bSnapRect)860 awt::Rectangle ChartView::getRectangleOfObject( const OUString& rObjectCID, bool bSnapRect )
861 {
862 impl_updateView();
863
864 awt::Rectangle aRet;
865 rtl::Reference< SvxShape > xShape = getShapeForCID(rObjectCID);
866 if(xShape.is())
867 {
868 //special handling for axis for old api:
869 //same special handling for diagram
870 ObjectType eObjectType( ObjectIdentifier::getObjectType( rObjectCID ) );
871 if( eObjectType == OBJECTTYPE_AXIS || eObjectType == OBJECTTYPE_DIAGRAM )
872 {
873 SolarMutexGuard aSolarGuard;
874 SdrObject* pRootSdrObject = xShape->GetSdrObject();
875 if( pRootSdrObject )
876 {
877 SdrObjList* pRootList = pRootSdrObject->GetSubList();
878 if( pRootList )
879 {
880 OUString aShapeName = u"MarkHandles"_ustr;
881 if( eObjectType == OBJECTTYPE_DIAGRAM )
882 aShapeName = "PlotAreaIncludingAxes";
883 SdrObject* pShape = DrawModelWrapper::getNamedSdrObject( aShapeName, pRootList );
884 if( pShape )
885 {
886 xShape = dynamic_cast<SvxShape*>(pShape->getUnoShape().get());
887 assert(xShape);
888 }
889 }
890 }
891 }
892
893 awt::Size aSize( xShape->getSize() );
894 awt::Point aPoint( xShape->getPosition() );
895 aRet = awt::Rectangle( aPoint.X, aPoint.Y, aSize.Width, aSize.Height );
896 if( bSnapRect )
897 {
898 //for rotated objects the shape size and position differs from the visible rectangle
899 SdrObject* pSdrObject = xShape->GetSdrObject();
900 if( pSdrObject )
901 {
902 tools::Rectangle aSnapRect( pSdrObject->GetSnapRect() );
903 aRet = awt::Rectangle(aSnapRect.Left(),aSnapRect.Top(),aSnapRect.GetWidth(),aSnapRect.GetHeight());
904 }
905 }
906 }
907 return aRet;
908 }
909
getDrawModelWrapper()910 std::shared_ptr< DrawModelWrapper > ChartView::getDrawModelWrapper()
911 {
912 return m_pDrawModelWrapper;
913 }
914
915 namespace
916 {
917
918 constexpr double constPageLayoutDistancePercentage = 0.02;
919 constexpr sal_Int32 constPageLayoutFixedDistance = 350;
920
getAvailablePosAndSizeForDiagram(CreateShapeParam2D & rParam,const awt::Size & rPageSize,rtl::Reference<Diagram> const & xDiagram)921 bool getAvailablePosAndSizeForDiagram(
922 CreateShapeParam2D& rParam, const awt::Size & rPageSize, rtl::Reference<Diagram> const& xDiagram)
923 {
924 uno::Reference<beans::XPropertySet> const& xProp(xDiagram);
925 rParam.mbUseFixedInnerSize = false;
926
927 //@todo: we need a size dependent on the axis labels
928 rtl::Reference<ChartType> xChartType;
929 if (xDiagram)
930 xChartType = xDiagram->getChartTypeByIndex(0);
931
932 sal_Int32 nXDistance = sal_Int32(rPageSize.Width * constPageLayoutDistancePercentage);
933 sal_Int32 nYDistance = sal_Int32(rPageSize.Height * constPageLayoutDistancePercentage);
934
935 // Only pie chart uses fixed size margins
936 if (xChartType.is() && xChartType->getChartType() == CHART2_SERVICE_NAME_CHARTTYPE_PIE)
937 {
938 nXDistance = constPageLayoutFixedDistance;
939 nYDistance = constPageLayoutFixedDistance;
940 }
941
942 rParam.maRemainingSpace.X += nXDistance;
943 rParam.maRemainingSpace.Width -= 2*nXDistance;
944 rParam.maRemainingSpace.Y += nYDistance;
945 rParam.maRemainingSpace.Height -= 2*nYDistance;
946
947 bool bPosSizeExcludeAxes = false;
948 if( xProp.is() )
949 xProp->getPropertyValue( u"PosSizeExcludeAxes"_ustr ) >>= bPosSizeExcludeAxes;
950
951 //size:
952 css::chart2::RelativeSize aRelativeSize;
953 if( xProp.is() && (xProp->getPropertyValue( u"RelativeSize"_ustr )>>=aRelativeSize) )
954 {
955 rParam.maRemainingSpace.Height = static_cast<sal_Int32>(aRelativeSize.Secondary*rPageSize.Height);
956 rParam.maRemainingSpace.Width = static_cast<sal_Int32>(aRelativeSize.Primary*rPageSize.Width);
957 rParam.mbUseFixedInnerSize = bPosSizeExcludeAxes;
958 }
959
960 if (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0)
961 return false;
962
963 //position:
964 chart2::RelativePosition aRelativePosition;
965 if( xProp.is() && (xProp->getPropertyValue( u"RelativePosition"_ustr )>>=aRelativePosition) )
966 {
967 //@todo decide whether x is primary or secondary
968
969 //the coordinates re relative to the page
970 double fX = aRelativePosition.Primary*rPageSize.Width;
971 double fY = aRelativePosition.Secondary*rPageSize.Height;
972
973 awt::Point aPos = RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
974 awt::Point(static_cast<sal_Int32>(fX),static_cast<sal_Int32>(fY)),
975 awt::Size(rParam.maRemainingSpace.Width, rParam.maRemainingSpace.Height),
976 aRelativePosition.Anchor);
977
978 rParam.maRemainingSpace.X = aPos.X;
979 rParam.maRemainingSpace.Y = aPos.Y;
980
981 rParam.mbUseFixedInnerSize = bPosSizeExcludeAxes;
982 }
983
984 //ensure that the diagram does not lap out right side or out of bottom
985 if (rParam.maRemainingSpace.Y + rParam.maRemainingSpace.Height > rPageSize.Height)
986 rParam.maRemainingSpace.Height = rPageSize.Height - rParam.maRemainingSpace.Y;
987
988 if (rParam.maRemainingSpace.X + rParam.maRemainingSpace.Width > rPageSize.Width)
989 rParam.maRemainingSpace.Width = rPageSize.Width - rParam.maRemainingSpace.X;
990
991 return true;
992 }
993
994 enum class TitleAlignment { ALIGN_LEFT, ALIGN_TOP, ALIGN_RIGHT, ALIGN_BOTTOM, ALIGN_Z };
995
changePositionOfAxisTitle(VTitle * pVTitle,TitleAlignment eAlignment,awt::Rectangle const & rDiagramPlusAxesRect,const awt::Size & rPageSize)996 void changePositionOfAxisTitle( VTitle* pVTitle, TitleAlignment eAlignment
997 , awt::Rectangle const & rDiagramPlusAxesRect, const awt::Size & rPageSize )
998 {
999 if(!pVTitle)
1000 return;
1001
1002 awt::Point aNewPosition(0,0);
1003 awt::Size aTitleSize = pVTitle->getFinalSize();
1004 sal_Int32 nYDistance = static_cast<sal_Int32>(rPageSize.Height * constPageLayoutDistancePercentage);
1005 sal_Int32 nXDistance = static_cast<sal_Int32>(rPageSize.Width * constPageLayoutDistancePercentage);
1006 switch (eAlignment)
1007 {
1008 case TitleAlignment::ALIGN_TOP:
1009 aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width/2
1010 , rDiagramPlusAxesRect.Y - aTitleSize.Height/2 - nYDistance );
1011 break;
1012 case TitleAlignment::ALIGN_BOTTOM:
1013 aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width/2
1014 , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height + aTitleSize.Height/2 + nYDistance );
1015 break;
1016 case TitleAlignment::ALIGN_LEFT:
1017 aNewPosition = awt::Point( rDiagramPlusAxesRect.X - aTitleSize.Width/2 - nXDistance
1018 , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height/2 );
1019 break;
1020 case TitleAlignment::ALIGN_RIGHT:
1021 aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width + aTitleSize.Width/2 + nXDistance
1022 , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height/2 );
1023 break;
1024 case TitleAlignment::ALIGN_Z:
1025 aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width + aTitleSize.Width/2 + nXDistance
1026 , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height - aTitleSize.Height/2 );
1027 break;
1028 }
1029
1030 sal_Int32 nMaxY = rPageSize.Height - aTitleSize.Height/2;
1031 sal_Int32 nMaxX = rPageSize.Width - aTitleSize.Width/2;
1032 sal_Int32 nMinX = aTitleSize.Width/2;
1033 sal_Int32 nMinY = aTitleSize.Height/2;
1034 if( aNewPosition.Y > nMaxY )
1035 aNewPosition.Y = nMaxY;
1036 if( aNewPosition.X > nMaxX )
1037 aNewPosition.X = nMaxX;
1038 if( aNewPosition.Y < nMinY )
1039 aNewPosition.Y = nMinY;
1040 if( aNewPosition.X < nMinX )
1041 aNewPosition.X = nMinX;
1042
1043 pVTitle->changePosition( aNewPosition );
1044 }
1045
lcl_createTitle(TitleHelper::eTitleType eType,const rtl::Reference<SvxShapeGroupAnyD> & xPageShapes,ChartModel & rModel,awt::Rectangle & rRemainingSpace,const awt::Size & rPageSize,TitleAlignment eAlignment,bool & rbAutoPosition)1046 std::shared_ptr<VTitle> lcl_createTitle( TitleHelper::eTitleType eType
1047 , const rtl::Reference<SvxShapeGroupAnyD>& xPageShapes
1048 , ChartModel& rModel
1049 , awt::Rectangle& rRemainingSpace
1050 , const awt::Size & rPageSize
1051 , TitleAlignment eAlignment
1052 , bool& rbAutoPosition )
1053 {
1054 std::shared_ptr<VTitle> apVTitle;
1055
1056 // #i109336# Improve auto positioning in chart
1057 double fPercentage = constPageLayoutDistancePercentage;
1058 sal_Int32 nXDistance = static_cast< sal_Int32 >( rPageSize.Width * fPercentage );
1059 sal_Int32 nYDistance = static_cast< sal_Int32 >( rPageSize.Height * fPercentage );
1060 if ( eType == TitleHelper::MAIN_TITLE )
1061 {
1062 nYDistance += 135; // 1/100 mm
1063 }
1064 else if ( eType == TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION )
1065 {
1066 nYDistance = 420; // 1/100 mm
1067 }
1068 else if ( eType == TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION )
1069 {
1070 nXDistance = 450; // 1/100 mm
1071 }
1072
1073 rtl::Reference< Title > xTitle( TitleHelper::getTitle( eType, rModel ) );
1074 OUString aCompleteString = TitleHelper::getCompleteString(xTitle);
1075 if (aCompleteString.isEmpty() || !VTitle::isVisible(xTitle))
1076 return apVTitle;
1077
1078 //create title
1079 awt::Size aTextMaxWidth(rPageSize.Width, rPageSize.Height);
1080 bool bYAxisTitle = false;
1081 if (eType == TitleHelper::MAIN_TITLE || eType == TitleHelper::SUB_TITLE)
1082 {
1083 aTextMaxWidth.Width = static_cast<sal_Int32>(rPageSize.Width * 0.8);
1084 aTextMaxWidth.Height = static_cast<sal_Int32>(rPageSize.Height * 0.5);
1085 }
1086 else if (eType == TitleHelper::X_AXIS_TITLE || eType == TitleHelper::SECONDARY_X_AXIS_TITLE
1087 || eType == TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION)
1088 {
1089 aTextMaxWidth.Width = static_cast<sal_Int32>(rPageSize.Width * 0.8);
1090 aTextMaxWidth.Height = static_cast<sal_Int32>(rPageSize.Height * 0.2);
1091 }
1092 else if (eType == TitleHelper::Y_AXIS_TITLE || eType == TitleHelper::SECONDARY_Y_AXIS_TITLE
1093 || eType == TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION)
1094 {
1095 aTextMaxWidth.Width = static_cast<sal_Int32>(rPageSize.Width * 0.2);
1096 aTextMaxWidth.Height = static_cast<sal_Int32>(rPageSize.Height * 0.8);
1097 bYAxisTitle = true;
1098 }
1099 apVTitle = std::make_shared<VTitle>(xTitle);
1100 OUString aCID = ObjectIdentifier::createClassifiedIdentifierForObject(xTitle, &rModel);
1101 apVTitle->init(xPageShapes, aCID);
1102 apVTitle->createShapes(awt::Point(0, 0), rPageSize, aTextMaxWidth, bYAxisTitle);
1103 awt::Size aTitleUnrotatedSize = apVTitle->getUnrotatedSize();
1104 awt::Size aTitleSize = apVTitle->getFinalSize();
1105
1106 //position
1107 rbAutoPosition = true;
1108 awt::Point aNewPosition(0,0);
1109 chart2::RelativePosition aRelativePosition;
1110 if (xTitle.is() && (xTitle->getPropertyValue(u"RelativePosition"_ustr) >>= aRelativePosition))
1111 {
1112 rbAutoPosition = false;
1113
1114 //@todo decide whether x is primary or secondary
1115 double fX = aRelativePosition.Primary*rPageSize.Width;
1116 double fY = aRelativePosition.Secondary*rPageSize.Height;
1117
1118 double fAnglePi = apVTitle->getRotationAnglePi();
1119 aNewPosition = RelativePositionHelper::getCenterOfAnchoredObject(
1120 awt::Point(static_cast<sal_Int32>(fX),static_cast<sal_Int32>(fY))
1121 , aTitleUnrotatedSize, aRelativePosition.Anchor, fAnglePi );
1122
1123 const bool bInfiniteY = std::isinf(aRelativePosition.Secondary);
1124 if (bInfiniteY)
1125 {
1126 SAL_WARN("chart2", "infinite aRelativePosition.Secondary position, using ALIGN_BOTTOM");
1127 aNewPosition.Y = rRemainingSpace.Y + rRemainingSpace.Height - aTitleSize.Height/2 - nYDistance;
1128 }
1129
1130 const bool bInfiniteX = std::isinf(aRelativePosition.Primary);
1131 if (bInfiniteX)
1132 {
1133 SAL_WARN("chart2", "infinite aRelativePosition.Primary position, using ALIGN_RIGHT");
1134 aNewPosition.X = rRemainingSpace.X + rRemainingSpace.Width - aTitleSize.Width/2 - nXDistance;
1135 }
1136 }
1137 else //auto position
1138 {
1139 switch( eAlignment )
1140 {
1141 case TitleAlignment::ALIGN_TOP:
1142 aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width/2
1143 , rRemainingSpace.Y + aTitleSize.Height/2 + nYDistance );
1144 break;
1145 case TitleAlignment::ALIGN_BOTTOM:
1146 aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width/2
1147 , rRemainingSpace.Y + rRemainingSpace.Height - aTitleSize.Height/2 - nYDistance );
1148 break;
1149 case TitleAlignment::ALIGN_LEFT:
1150 aNewPosition = awt::Point( rRemainingSpace.X + aTitleSize.Width/2 + nXDistance
1151 , rRemainingSpace.Y + rRemainingSpace.Height/2 );
1152 break;
1153 case TitleAlignment::ALIGN_RIGHT:
1154 aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width - aTitleSize.Width/2 - nXDistance
1155 , rRemainingSpace.Y + rRemainingSpace.Height/2 );
1156 break;
1157 case TitleAlignment::ALIGN_Z:
1158 break;
1159
1160 }
1161 }
1162 apVTitle->changePosition( aNewPosition );
1163
1164 //remaining space
1165 switch( eAlignment )
1166 {
1167 case TitleAlignment::ALIGN_TOP:
1168 // Push the remaining space down from top.
1169 rRemainingSpace.Y += ( aTitleSize.Height + nYDistance );
1170 rRemainingSpace.Height -= ( aTitleSize.Height + nYDistance );
1171 break;
1172 case TitleAlignment::ALIGN_BOTTOM:
1173 // Push the remaining space up from bottom.
1174 rRemainingSpace.Height -= ( aTitleSize.Height + nYDistance );
1175 break;
1176 case TitleAlignment::ALIGN_LEFT:
1177 // Push the remaining space to the right from left edge.
1178 rRemainingSpace.X += ( aTitleSize.Width + nXDistance );
1179 rRemainingSpace.Width -= ( aTitleSize.Width + nXDistance );
1180 break;
1181 case TitleAlignment::ALIGN_RIGHT:
1182 // Push the remaining space to the left from right edge.
1183 rRemainingSpace.Width -= ( aTitleSize.Width + nXDistance );
1184 break;
1185 case TitleAlignment::ALIGN_Z:
1186 break;
1187 }
1188
1189 return apVTitle;
1190 }
1191
lcl_createLegend(const rtl::Reference<Legend> & xLegend,const rtl::Reference<SvxShapeGroupAnyD> & xPageShapes,const uno::Reference<uno::XComponentContext> & xContext,awt::Rectangle & rRemainingSpace,const awt::Size & rPageSize,ChartModel & rModel,std::vector<LegendEntryProvider * > && rLegendEntryProviderList,sal_Int16 nDefaultWritingMode)1192 bool lcl_createLegend( const rtl::Reference< Legend > & xLegend
1193 , const rtl::Reference<SvxShapeGroupAnyD>& xPageShapes
1194 , const uno::Reference< uno::XComponentContext > & xContext
1195 , awt::Rectangle & rRemainingSpace
1196 , const awt::Size & rPageSize
1197 , ChartModel& rModel
1198 , std::vector< LegendEntryProvider* >&& rLegendEntryProviderList
1199 , sal_Int16 nDefaultWritingMode )
1200 {
1201 if (!VLegend::isVisible(xLegend))
1202 return false;
1203
1204 awt::Size rDefaultLegendSize;
1205 VLegend aVLegend( xLegend, xContext, std::move(rLegendEntryProviderList),
1206 xPageShapes, rModel);
1207 aVLegend.setDefaultWritingMode( nDefaultWritingMode );
1208 aVLegend.createShapes( awt::Size( rRemainingSpace.Width, rRemainingSpace.Height ),
1209 rPageSize, rDefaultLegendSize );
1210 aVLegend.changePosition( rRemainingSpace, rPageSize, rDefaultLegendSize );
1211 return true;
1212 }
1213
lcl_createButtons(const rtl::Reference<SvxShapeGroupAnyD> & xPageShapes,ChartModel & rModel,awt::Rectangle & rRemainingSpace)1214 void lcl_createButtons(const rtl::Reference<SvxShapeGroupAnyD>& xPageShapes,
1215 ChartModel& rModel,
1216 awt::Rectangle& rRemainingSpace)
1217 {
1218 uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(rModel.getDataProvider(), uno::UNO_QUERY);
1219 if (!xPivotTableDataProvider.is())
1220 return;
1221
1222 uno::Reference<beans::XPropertySet> xModelPage(rModel.getPageBackground());
1223
1224 awt::Size aSize(4000, 700); // size of the button
1225
1226 tools::Long x = 0;
1227
1228 if (xPivotTableDataProvider->getPageFields().hasElements())
1229 {
1230 x = 0;
1231
1232 const css::uno::Sequence<chart2::data::PivotTableFieldEntry> aPivotFieldEntries = xPivotTableDataProvider->getPageFields();
1233 for (css::chart2::data::PivotTableFieldEntry const & rPageFieldEntry : aPivotFieldEntries)
1234 {
1235 VButton aButton;
1236 aButton.init(xPageShapes);
1237 awt::Point aNewPosition(rRemainingSpace.X + x + 100, rRemainingSpace.Y + 100);
1238 sal_Int32 nDimensionIndex = rPageFieldEntry.DimensionIndex;
1239 OUString aFieldOutputDescription = xPivotTableDataProvider->getFieldOutputDescription(nDimensionIndex);
1240 aButton.setLabel(rPageFieldEntry.Name + " | " + aFieldOutputDescription);
1241 aButton.setCID("FieldButton.Page." + OUString::number(nDimensionIndex));
1242 aButton.setPosition(aNewPosition);
1243 aButton.setSize(aSize);
1244 if (rPageFieldEntry.HasHiddenMembers)
1245 aButton.setArrowColor(Color(0x0000FF));
1246
1247 aButton.createShapes(xModelPage);
1248 x += aSize.Width + 100;
1249 }
1250 rRemainingSpace.Y += (aSize.Height + 100 + 100);
1251 rRemainingSpace.Height -= (aSize.Height + 100 + 100);
1252 }
1253
1254 aSize = awt::Size(3000, 700); // size of the button
1255
1256 if (!xPivotTableDataProvider->getRowFields().hasElements())
1257 return;
1258
1259 x = 200;
1260 const css::uno::Sequence<chart2::data::PivotTableFieldEntry> aPivotFieldEntries = xPivotTableDataProvider->getRowFields();
1261 for (css::chart2::data::PivotTableFieldEntry const & rRowFieldEntry : aPivotFieldEntries)
1262 {
1263 VButton aButton;
1264 aButton.init(xPageShapes);
1265 awt::Point aNewPosition(rRemainingSpace.X + x + 100,
1266 rRemainingSpace.Y + rRemainingSpace.Height - aSize.Height - 100);
1267 aButton.setLabel(rRowFieldEntry.Name);
1268 aButton.setCID("FieldButton.Row." + OUString::number(rRowFieldEntry.DimensionIndex));
1269 aButton.setPosition(aNewPosition);
1270 aButton.setSize(aSize);
1271 if ( rRowFieldEntry.Name == "Data" )
1272 {
1273 aButton.setBGColor( Color(0x00F6F6F6) );
1274 aButton.showArrow( false );
1275 }
1276 else if (rRowFieldEntry.HasHiddenMembers)
1277 aButton.setArrowColor(Color(0x0000FF));
1278 aButton.createShapes(xModelPage);
1279 x += aSize.Width + 100;
1280 }
1281 rRemainingSpace.Height -= (aSize.Height + 100 + 100);
1282 }
1283
formatPage(ChartModel & rChartModel,const awt::Size & rPageSize,const rtl::Reference<SvxShapeGroupAnyD> & xTarget)1284 void formatPage(
1285 ChartModel& rChartModel
1286 , const awt::Size& rPageSize
1287 , const rtl::Reference<SvxShapeGroupAnyD>& xTarget
1288 )
1289 {
1290 try
1291 {
1292 uno::Reference< beans::XPropertySet > xModelPage( rChartModel.getPageBackground());
1293 if( ! xModelPage.is())
1294 return;
1295
1296 //format page
1297 tPropertyNameValueMap aNameValueMap;
1298 PropertyMapper::getValueMap( aNameValueMap, PropertyMapper::getPropertyNameMapForFillAndLineProperties(), xModelPage );
1299
1300 OUString aCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, u"" ) );
1301 aNameValueMap.emplace( "Name", uno::Any( aCID ) ); //CID OUString
1302
1303 tNameSequence aNames;
1304 tAnySequence aValues;
1305 PropertyMapper::getMultiPropertyListsFromValueMap( aNames, aValues, aNameValueMap );
1306
1307 ShapeFactory::createRectangle(
1308 xTarget, rPageSize, awt::Point(0, 0), aNames, aValues);
1309 }
1310 catch( const uno::Exception & )
1311 {
1312 DBG_UNHANDLED_EXCEPTION("chart2" );
1313 }
1314 }
1315
lcl_removeEmptyGroupShapes(const SdrObject & rParent)1316 void lcl_removeEmptyGroupShapes( const SdrObject& rParent )
1317 {
1318 SdrObjList* pObjList = rParent.getChildrenOfSdrObject();
1319 if (!pObjList || pObjList->GetObjCount() == 0)
1320 return;
1321
1322 //iterate from back!
1323 for(auto nIdx = static_cast<sal_Int32>(pObjList->GetObjCount() - 1); nIdx >= 0; --nIdx)
1324 {
1325 SdrObject* pChildSdrObject = pObjList->GetObj(nIdx);
1326 SdrObjList* pChildObjList = pChildSdrObject->getChildrenOfSdrObject();
1327 if (!pChildObjList)
1328 continue;
1329 if (pChildObjList->GetObjCount() == 0)
1330 {
1331 //remove empty group shape
1332 pObjList->NbcRemoveObject(nIdx);
1333 }
1334 else
1335 lcl_removeEmptyGroupShapes(*pChildSdrObject);
1336 }
1337 }
1338
1339 }
1340
impl_refreshAddIn()1341 void ChartView::impl_refreshAddIn()
1342 {
1343 if( !m_bRefreshAddIn )
1344 return;
1345
1346 uno::Reference< beans::XPropertySet > xProp( static_cast< ::cppu::OWeakObject* >( &mrChartModel ), uno::UNO_QUERY );
1347 if( !xProp.is())
1348 return;
1349
1350 try
1351 {
1352 uno::Reference< util::XRefreshable > xAddIn;
1353 xProp->getPropertyValue( u"AddIn"_ustr ) >>= xAddIn;
1354 if( xAddIn.is() )
1355 {
1356 bool bRefreshAddInAllowed = true;
1357 xProp->getPropertyValue( u"RefreshAddInAllowed"_ustr ) >>= bRefreshAddInAllowed;
1358 if( bRefreshAddInAllowed )
1359 xAddIn->refresh();
1360 }
1361 }
1362 catch( const uno::Exception& )
1363 {
1364 TOOLS_WARN_EXCEPTION("chart2", "" );
1365 }
1366 }
1367
createShapes()1368 void ChartView::createShapes()
1369 {
1370 SolarMutexGuard aSolarGuard;
1371
1372 std::unique_lock aTimedGuard(maTimeMutex);
1373 if(mrChartModel.isTimeBased())
1374 {
1375 maTimeBased.bTimeBased = true;
1376 }
1377
1378 //make sure add-in is refreshed after creating the shapes
1379 const ::comphelper::ScopeGuard aGuard( [this]() { this->impl_refreshAddIn(); } );
1380
1381 m_aResultingDiagramRectangleExcludingAxes = awt::Rectangle(0,0,0,0);
1382 impl_deleteCoordinateSystems();
1383 if( m_pDrawModelWrapper )
1384 {
1385 // #i12587# support for shapes in chart
1386 m_pDrawModelWrapper->getSdrModel().EnableUndo( false );
1387 m_pDrawModelWrapper->clearMainDrawPage();
1388 }
1389
1390 lcl_setDefaultWritingMode( m_pDrawModelWrapper, mrChartModel );
1391
1392 awt::Size aPageSize = mrChartModel.getVisualAreaSize( embed::Aspects::MSOLE_CONTENT );
1393
1394 if(!mxRootShape.is())
1395 mxRootShape = ShapeFactory::getOrCreateChartRootShape( m_xDrawPage );
1396
1397 SdrPage* pPage = getSdrPage();
1398
1399 if (pPage) //it is necessary to use the implementation here as the uno page does not provide a propertyset
1400 {
1401 pPage->SetSize(Size(aPageSize.Width,aPageSize.Height));
1402 }
1403 else
1404 {
1405 OSL_FAIL("could not set page size correctly");
1406 }
1407 ShapeFactory::setPageSize(mxRootShape, aPageSize);
1408
1409 createShapes2D(aPageSize);
1410
1411 // #i12587# support for shapes in chart
1412 if ( m_pDrawModelWrapper )
1413 {
1414 m_pDrawModelWrapper->getSdrModel().EnableUndo( true );
1415 }
1416
1417 if(maTimeBased.bTimeBased)
1418 {
1419 maTimeBased.nFrame++;
1420 }
1421 }
1422
1423 // util::XEventListener (base of XCloseListener)
disposing(const lang::EventObject &)1424 void SAL_CALL ChartView::disposing( const lang::EventObject& /* rSource */ )
1425 {
1426 }
1427
1428 namespace
1429 {
1430 // Disables setting the chart's modified state, as well as its parent's (if exists).
1431 // Painting a chart must not set these states.
1432 struct ChartModelDisableSetModified
1433 {
1434 ChartModel& mrChartModel;
1435 SfxObjectShell* mpParentShell;
1436 bool mbWasUnmodified;
ChartModelDisableSetModifiedchart::__anonea4629340511::ChartModelDisableSetModified1437 ChartModelDisableSetModified(ChartModel& rChartModel)
1438 : mrChartModel(rChartModel)
1439 , mpParentShell(SfxObjectShell::GetShellFromComponent(rChartModel.getParent()))
1440 , mbWasUnmodified(!rChartModel.isModified())
1441 {
1442 if (mpParentShell && mpParentShell->IsEnableSetModified())
1443 mpParentShell->EnableSetModified(false);
1444 else
1445 mpParentShell = nullptr;
1446 }
~ChartModelDisableSetModifiedchart::__anonea4629340511::ChartModelDisableSetModified1447 ~ChartModelDisableSetModified()
1448 {
1449 if (mbWasUnmodified && mrChartModel.isModified())
1450 mrChartModel.setModified(false);
1451 if (mpParentShell)
1452 mpParentShell->EnableSetModified(true);
1453 }
1454 };
1455 }
1456
impl_updateView(bool bCheckLockedCtrler)1457 void ChartView::impl_updateView( bool bCheckLockedCtrler )
1458 {
1459 if( !m_pDrawModelWrapper )
1460 return;
1461
1462 // #i12587# support for shapes in chart
1463 if ( m_bSdrViewIsInEditMode )
1464 {
1465 return;
1466 }
1467
1468 if (bCheckLockedCtrler && mrChartModel.hasControllersLocked())
1469 return;
1470
1471 if( !m_bViewDirty || m_bInViewUpdate )
1472 return;
1473
1474 m_bInViewUpdate = true;
1475 //bool bOldRefreshAddIn = m_bRefreshAddIn;
1476 //m_bRefreshAddIn = false;
1477 try
1478 {
1479 impl_notifyModeChangeListener(u"invalid"_ustr);
1480
1481 //prepare draw model
1482 {
1483 SolarMutexGuard aSolarGuard;
1484 m_pDrawModelWrapper->lockControllers();
1485 }
1486
1487 // Rendering the chart must not set its (or its parent) modified status
1488 ChartModelDisableSetModified dontSetModified(mrChartModel);
1489
1490 //create chart view
1491 {
1492 m_bViewDirty = false;
1493 m_bViewUpdatePending = false;
1494 createShapes();
1495
1496 if( m_bViewDirty )
1497 {
1498 //avoid recursions due to add-in
1499 m_bRefreshAddIn = false;
1500 m_bViewDirty = false;
1501 m_bViewUpdatePending = false;
1502 //delete old chart view
1503 createShapes();
1504 m_bRefreshAddIn = true;
1505 }
1506 }
1507
1508 m_bViewDirty = m_bViewUpdatePending;
1509 m_bViewUpdatePending = false;
1510 m_bInViewUpdate = false;
1511 }
1512 catch( const uno::Exception& )
1513 {
1514 DBG_UNHANDLED_EXCEPTION("chart2" );
1515 m_bViewDirty = m_bViewUpdatePending;
1516 m_bViewUpdatePending = false;
1517 m_bInViewUpdate = false;
1518 }
1519
1520 {
1521 SolarMutexGuard aSolarGuard;
1522 m_pDrawModelWrapper->unlockControllers();
1523 }
1524
1525 impl_notifyModeChangeListener(u"valid"_ustr);
1526
1527 //m_bRefreshAddIn = bOldRefreshAddIn;
1528 }
1529
1530 // ____ XModifyListener ____
modified(const lang::EventObject &)1531 void SAL_CALL ChartView::modified( const lang::EventObject& /* aEvent */ )
1532 {
1533 m_bViewDirty = true;
1534 if( m_bInViewUpdate )
1535 m_bViewUpdatePending = true;
1536
1537 impl_notifyModeChangeListener(u"dirty"_ustr);
1538 }
1539
1540 //SfxListener
Notify(SfxBroadcaster &,const SfxHint & rHint)1541 void ChartView::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
1542 {
1543 //#i77362 change notification for changes on additional shapes are missing
1544 if( m_bInViewUpdate )
1545 return;
1546
1547 // #i12587# support for shapes in chart
1548 if ( m_bSdrViewIsInEditMode )
1549 {
1550 uno::Reference< view::XSelectionSupplier > xSelectionSupplier( mrChartModel.getCurrentController(), uno::UNO_QUERY );
1551 if ( xSelectionSupplier.is() )
1552 {
1553 OUString aSelObjCID;
1554 uno::Any aSelObj( xSelectionSupplier->getSelection() );
1555 aSelObj >>= aSelObjCID;
1556 if ( !aSelObjCID.isEmpty() )
1557 {
1558 return;
1559 }
1560 }
1561 }
1562
1563 if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
1564 return;
1565 const SdrHint* pSdrHint = static_cast< const SdrHint* >(&rHint);
1566
1567 bool bShapeChanged = false;
1568 switch( pSdrHint->GetKind() )
1569 {
1570 case SdrHintKind::ObjectChange:
1571 bShapeChanged = true;
1572 break;
1573 case SdrHintKind::ObjectInserted:
1574 bShapeChanged = true;
1575 break;
1576 case SdrHintKind::ObjectRemoved:
1577 bShapeChanged = true;
1578 break;
1579 case SdrHintKind::ModelCleared:
1580 bShapeChanged = true;
1581 break;
1582 case SdrHintKind::EndEdit:
1583 bShapeChanged = true;
1584 break;
1585 default:
1586 break;
1587 }
1588
1589 if(bShapeChanged)
1590 {
1591 //#i76053# do not send view modified notifications for changes on the hidden page which contains e.g. the symbols for the dialogs
1592 if( ChartView::getSdrPage() != pSdrHint->GetPage() )
1593 bShapeChanged=false;
1594 }
1595
1596 if(!bShapeChanged)
1597 return;
1598
1599 mrChartModel.setModified(true);
1600 }
1601
impl_notifyModeChangeListener(const OUString & rNewMode)1602 void ChartView::impl_notifyModeChangeListener( const OUString& rNewMode )
1603 {
1604 try
1605 {
1606 std::unique_lock g(m_aMutex);
1607 if( m_aModeChangeListeners.getLength(g) )
1608 {
1609 util::ModeChangeEvent aEvent( static_cast< uno::XWeak* >( this ), rNewMode );
1610 m_aModeChangeListeners.notifyEach( g, &css::util::XModeChangeListener::modeChanged, aEvent);
1611 }
1612 }
1613 catch( const uno::Exception& )
1614 {
1615 DBG_UNHANDLED_EXCEPTION("chart2");
1616 }
1617 }
1618
1619 // ____ XModeChangeBroadcaster ____
1620
addModeChangeListener(const uno::Reference<util::XModeChangeListener> & xListener)1621 void SAL_CALL ChartView::addModeChangeListener( const uno::Reference< util::XModeChangeListener >& xListener )
1622 {
1623 std::unique_lock g(m_aMutex);
1624 m_aModeChangeListeners.addInterface(g, xListener );
1625 }
removeModeChangeListener(const uno::Reference<util::XModeChangeListener> & xListener)1626 void SAL_CALL ChartView::removeModeChangeListener( const uno::Reference< util::XModeChangeListener >& xListener )
1627 {
1628 std::unique_lock g(m_aMutex);
1629 m_aModeChangeListeners.removeInterface(g, xListener );
1630 }
addModeChangeApproveListener(const uno::Reference<util::XModeChangeApproveListener> &)1631 void SAL_CALL ChartView::addModeChangeApproveListener( const uno::Reference< util::XModeChangeApproveListener >& /* _rxListener */ )
1632 {
1633
1634 }
removeModeChangeApproveListener(const uno::Reference<util::XModeChangeApproveListener> &)1635 void SAL_CALL ChartView::removeModeChangeApproveListener( const uno::Reference< util::XModeChangeApproveListener >& /* _rxListener */ )
1636 {
1637
1638 }
1639
1640 // ____ XUpdatable ____
update()1641 void SAL_CALL ChartView::update()
1642 {
1643 impl_updateView();
1644
1645 //#i100778# migrate all imported or old documents to a plot area sizing exclusive axes (in case the save settings allow for this):
1646 //Although in general it is a bad idea to change the model from within the view this is exceptionally the best place to do this special conversion.
1647 //When a view update is requested (what happens for creating the metafile or displaying
1648 //the chart in edit mode or printing) it is most likely that all necessary information is available - like the underlying spreadsheet data for example.
1649 //Those data are important for the correct axis label sizes which are needed during conversion.
1650 if( DiagramHelper::switchDiagramPositioningToExcludingPositioning( mrChartModel, true, false ) )
1651 impl_updateView();
1652 }
1653
updateSoft()1654 void SAL_CALL ChartView::updateSoft()
1655 {
1656 update();
1657 }
1658
updateHard()1659 void SAL_CALL ChartView::updateHard()
1660 {
1661 impl_updateView(false);
1662 }
1663
1664 // ____ XPropertySet ____
getPropertySetInfo()1665 Reference< beans::XPropertySetInfo > SAL_CALL ChartView::getPropertySetInfo()
1666 {
1667 OSL_FAIL("not implemented");
1668 return nullptr;
1669 }
1670
setPropertyValue(const OUString & rPropertyName,const Any & rValue)1671 void SAL_CALL ChartView::setPropertyValue( const OUString& rPropertyName
1672 , const Any& rValue )
1673 {
1674 if( rPropertyName == "Resolution" )
1675 {
1676 awt::Size aNewResolution;
1677 if( ! (rValue >>= aNewResolution) )
1678 throw lang::IllegalArgumentException( u"Property 'Resolution' requires value of type awt::Size"_ustr, nullptr, 0 );
1679
1680 if( m_aPageResolution.Width!=aNewResolution.Width || m_aPageResolution.Height!=aNewResolution.Height )
1681 {
1682 //set modified only when the new resolution is higher and points were skipped before
1683 bool bSetModified = m_bPointsWereSkipped && (m_aPageResolution.Width<aNewResolution.Width || m_aPageResolution.Height<aNewResolution.Height);
1684
1685 m_aPageResolution = aNewResolution;
1686
1687 if( bSetModified )
1688 this->modified( lang::EventObject( static_cast< uno::XWeak* >( this ) ) );
1689 }
1690 }
1691 else if( rPropertyName == "ZoomFactors" )
1692 {
1693 //#i75867# poor quality of ole's alternative view with 3D scenes and zoomfactors besides 100%
1694 uno::Sequence< beans::PropertyValue > aZoomFactors;
1695 if( ! (rValue >>= aZoomFactors) )
1696 throw lang::IllegalArgumentException( u"Property 'ZoomFactors' requires value of type Sequence< PropertyValue >"_ustr, nullptr, 0 );
1697
1698 for (auto& propval : aZoomFactors)
1699 {
1700 if (propval.Name == "ScaleXNumerator")
1701 propval.Value >>= m_nScaleXNumerator;
1702 else if (propval.Name == "ScaleXDenominator")
1703 propval.Value >>= m_nScaleXDenominator;
1704 else if (propval.Name == "ScaleYNumerator")
1705 propval.Value >>= m_nScaleYNumerator;
1706 else if (propval.Name == "ScaleYDenominator")
1707 propval.Value >>= m_nScaleYDenominator;
1708 }
1709 }
1710 else if( rPropertyName == "SdrViewIsInEditMode" )
1711 {
1712 //#i77362 change notification for changes on additional shapes are missing
1713 if( ! (rValue >>= m_bSdrViewIsInEditMode) )
1714 throw lang::IllegalArgumentException( u"Property 'SdrViewIsInEditMode' requires value of type sal_Bool"_ustr, nullptr, 0 );
1715 }
1716 else
1717 throw beans::UnknownPropertyException( "unknown property was tried to set to chart wizard " + rPropertyName, nullptr );
1718 }
1719
getPropertyValue(const OUString & rPropertyName)1720 Any SAL_CALL ChartView::getPropertyValue( const OUString& rPropertyName )
1721 {
1722 if( rPropertyName != "Resolution" )
1723 throw beans::UnknownPropertyException( "unknown property was tried to get from chart wizard " + rPropertyName, nullptr );
1724
1725 return Any(m_aPageResolution);
1726 }
1727
addPropertyChangeListener(const OUString &,const Reference<beans::XPropertyChangeListener> &)1728 void SAL_CALL ChartView::addPropertyChangeListener(
1729 const OUString& /* aPropertyName */, const Reference< beans::XPropertyChangeListener >& /* xListener */ )
1730 {
1731 OSL_FAIL("not implemented");
1732 }
removePropertyChangeListener(const OUString &,const Reference<beans::XPropertyChangeListener> &)1733 void SAL_CALL ChartView::removePropertyChangeListener(
1734 const OUString& /* aPropertyName */, const Reference< beans::XPropertyChangeListener >& /* aListener */ )
1735 {
1736 OSL_FAIL("not implemented");
1737 }
1738
addVetoableChangeListener(const OUString &,const Reference<beans::XVetoableChangeListener> &)1739 void SAL_CALL ChartView::addVetoableChangeListener( const OUString& /* PropertyName */, const Reference< beans::XVetoableChangeListener >& /* aListener */ )
1740 {
1741 OSL_FAIL("not implemented");
1742 }
1743
removeVetoableChangeListener(const OUString &,const Reference<beans::XVetoableChangeListener> &)1744 void SAL_CALL ChartView::removeVetoableChangeListener( const OUString& /* PropertyName */, const Reference< beans::XVetoableChangeListener >& /* aListener */ )
1745 {
1746 OSL_FAIL("not implemented");
1747 }
1748
1749 // ____ XMultiServiceFactory ____
1750
createInstance(const OUString & aServiceSpecifier)1751 Reference< uno::XInterface > ChartView::createInstance( const OUString& aServiceSpecifier )
1752 {
1753 SolarMutexGuard aSolarGuard;
1754
1755 SdrModel* pModel = ( m_pDrawModelWrapper ? &m_pDrawModelWrapper->getSdrModel() : nullptr );
1756 if ( pModel )
1757 {
1758 if ( aServiceSpecifier == "com.sun.star.drawing.DashTable" )
1759 {
1760 if ( !m_xDashTable.is() )
1761 {
1762 m_xDashTable = SvxUnoDashTable_createInstance( pModel );
1763 }
1764 return m_xDashTable;
1765 }
1766 else if ( aServiceSpecifier == "com.sun.star.drawing.GradientTable" )
1767 {
1768 if ( !m_xGradientTable.is() )
1769 {
1770 m_xGradientTable = SvxUnoGradientTable_createInstance( pModel );
1771 }
1772 return m_xGradientTable;
1773 }
1774 else if ( aServiceSpecifier == "com.sun.star.drawing.HatchTable" )
1775 {
1776 if ( !m_xHatchTable.is() )
1777 {
1778 m_xHatchTable = SvxUnoHatchTable_createInstance( pModel );
1779 }
1780 return m_xHatchTable;
1781 }
1782 else if ( aServiceSpecifier == "com.sun.star.drawing.BitmapTable" )
1783 {
1784 if ( !m_xBitmapTable.is() )
1785 {
1786 m_xBitmapTable = SvxUnoBitmapTable_createInstance( pModel );
1787 }
1788 return m_xBitmapTable;
1789 }
1790 else if ( aServiceSpecifier == "com.sun.star.drawing.TransparencyGradientTable" )
1791 {
1792 if ( !m_xTransGradientTable.is() )
1793 {
1794 m_xTransGradientTable = SvxUnoTransGradientTable_createInstance( pModel );
1795 }
1796 return m_xTransGradientTable;
1797 }
1798 else if ( aServiceSpecifier == "com.sun.star.drawing.MarkerTable" )
1799 {
1800 if ( !m_xMarkerTable.is() )
1801 {
1802 m_xMarkerTable = SvxUnoMarkerTable_createInstance( pModel );
1803 }
1804 return m_xMarkerTable;
1805 }
1806 }
1807
1808 return nullptr;
1809 }
1810
createInstanceWithArguments(const OUString & ServiceSpecifier,const uno::Sequence<uno::Any> & Arguments)1811 Reference< uno::XInterface > ChartView::createInstanceWithArguments( const OUString& ServiceSpecifier, const uno::Sequence< uno::Any >& Arguments )
1812 {
1813 OSL_ENSURE( Arguments.hasElements(), "ChartView::createInstanceWithArguments: arguments are ignored" );
1814 return createInstance( ServiceSpecifier );
1815 }
1816
getAvailableServiceNames()1817 uno::Sequence< OUString > ChartView::getAvailableServiceNames()
1818 {
1819 uno::Sequence< OUString > aServiceNames{ u"com.sun.star.drawing.DashTable"_ustr,
1820 u"com.sun.star.drawing.GradientTable"_ustr,
1821 u"com.sun.star.drawing.HatchTable"_ustr,
1822 u"com.sun.star.drawing.BitmapTable"_ustr,
1823 u"com.sun.star.drawing.TransparencyGradientTable"_ustr,
1824 u"com.sun.star.drawing.MarkerTable"_ustr };
1825
1826 return aServiceNames;
1827 }
1828
dump(OUString const & kind)1829 OUString ChartView::dump(OUString const & kind)
1830 {
1831 if (kind.isEmpty()) {
1832 return comphelper::dumpXmlToString([this](auto writer) { return dumpAsXml(writer); });
1833 }
1834
1835 // kind == "shapes":
1836 #if HAVE_FEATURE_DESKTOP
1837 // Used for unit tests and in chartcontroller only, no need to drag in this when cross-compiling
1838 // for non-desktop
1839 impl_updateView();
1840 sal_Int32 n = m_xDrawPage->getCount();
1841 OUStringBuffer aBuffer;
1842 for(sal_Int32 i = 0; i < n; ++i)
1843 {
1844 uno::Reference< drawing::XShapes > xShape(m_xDrawPage->getByIndex(i), uno::UNO_QUERY);
1845 if(xShape.is())
1846 {
1847 OUString aString = XShapeDumper::dump(uno::Reference<drawing::XShapes>(mxRootShape));
1848 aBuffer.append(aString);
1849 }
1850 else
1851 {
1852 uno::Reference< drawing::XShape > xSingleShape(m_xDrawPage->getByIndex(i), uno::UNO_QUERY);
1853 if(!xSingleShape.is())
1854 continue;
1855 OUString aString = XShapeDumper::dump(xSingleShape);
1856 aBuffer.append(aString);
1857 }
1858 aBuffer.append("\n\n");
1859 }
1860
1861 return aBuffer.makeStringAndClear();
1862 #else
1863 return OUString();
1864 #endif
1865 }
1866
dumpAsXml(xmlTextWriterPtr pWriter) const1867 void ChartView::dumpAsXml(xmlTextWriterPtr pWriter) const
1868 {
1869 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ChartView"));
1870 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
1871
1872 if (m_pDrawModelWrapper)
1873 {
1874 m_pDrawModelWrapper->dumpAsXml(pWriter);
1875 }
1876
1877 (void)xmlTextWriterEndElement(pWriter);
1878 }
1879
setViewDirty()1880 void ChartView::setViewDirty()
1881 {
1882 std::unique_lock aGuard(maTimeMutex);
1883 m_bViewDirty = true;
1884 }
1885
IMPL_LINK_NOARG(ChartView,UpdateTimeBased,Timer *,void)1886 IMPL_LINK_NOARG(ChartView, UpdateTimeBased, Timer *, void)
1887 {
1888 setViewDirty();
1889 update();
1890 }
1891
createShapes2D(const awt::Size & rPageSize)1892 void ChartView::createShapes2D( const awt::Size& rPageSize )
1893 {
1894 // todo: it would be nicer to just pass the page m_xDrawPage and format it,
1895 // but the draw page does not support XPropertySet
1896 formatPage( mrChartModel, rPageSize, mxRootShape );
1897
1898 CreateShapeParam2D aParam;
1899 aParam.maRemainingSpace.X = 0;
1900 aParam.maRemainingSpace.Y = 0;
1901 aParam.maRemainingSpace.Width = rPageSize.Width;
1902 aParam.maRemainingSpace.Height = rPageSize.Height;
1903
1904 //create the group shape for diagram and axes first to have title and legends on top of it
1905 rtl::Reference< Diagram > xDiagram( mrChartModel.getFirstChartDiagram() );
1906 bool bHasRelativeSize = false;
1907 if( xDiagram.is() && xDiagram->getPropertyValue(u"RelativeSize"_ustr).hasValue() )
1908 bHasRelativeSize = true;
1909
1910 OUString aDiagramCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM, OUString::number( 0 ) ) );//todo: other index if more than one diagram is possible
1911 rtl::Reference<SvxShapeGroup> xDiagramPlusAxesPlusMarkHandlesGroup_Shapes =
1912 ShapeFactory::createGroup2D(mxRootShape,aDiagramCID);
1913
1914 aParam.mxMarkHandles = ShapeFactory::createInvisibleRectangle(
1915 xDiagramPlusAxesPlusMarkHandlesGroup_Shapes, awt::Size(0,0));
1916 ShapeFactory::setShapeName(aParam.mxMarkHandles, u"MarkHandles"_ustr);
1917
1918 aParam.mxPlotAreaWithAxes = ShapeFactory::createInvisibleRectangle(
1919 xDiagramPlusAxesPlusMarkHandlesGroup_Shapes, awt::Size(0, 0));
1920 ShapeFactory::setShapeName(aParam.mxPlotAreaWithAxes, u"PlotAreaIncludingAxes"_ustr);
1921
1922 aParam.mxDiagramWithAxesShapes = ShapeFactory::createGroup2D(xDiagramPlusAxesPlusMarkHandlesGroup_Shapes);
1923
1924 bool bAutoPositionDummy = true;
1925
1926 // create buttons
1927 lcl_createButtons(mxRootShape, mrChartModel, aParam.maRemainingSpace);
1928
1929 lcl_createTitle(
1930 TitleHelper::MAIN_TITLE, mxRootShape, mrChartModel,
1931 aParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_TOP, bAutoPositionDummy);
1932 if (!bHasRelativeSize && (aParam.maRemainingSpace.Width <= 0 || aParam.maRemainingSpace.Height <= 0))
1933 return;
1934
1935 lcl_createTitle(
1936 TitleHelper::SUB_TITLE, mxRootShape, mrChartModel,
1937 aParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_TOP, bAutoPositionDummy );
1938 if (!bHasRelativeSize && (aParam.maRemainingSpace.Width <= 0 || aParam.maRemainingSpace.Height <= 0))
1939 return;
1940
1941 aParam.mpSeriesPlotterContainer = std::make_shared<SeriesPlotterContainer>(m_aVCooSysList);
1942 aParam.mpSeriesPlotterContainer->initializeCooSysAndSeriesPlotter( mrChartModel );
1943 if(maTimeBased.bTimeBased && maTimeBased.nFrame != 0)
1944 {
1945 auto& rSeriesPlotter = aParam.mpSeriesPlotterContainer->getSeriesPlotterList();
1946 size_t n = rSeriesPlotter.size();
1947 for(size_t i = 0; i < n; ++i)
1948 {
1949 std::vector<VDataSeries*> aAllNewDataSeries = rSeriesPlotter[i]->getAllSeries();
1950 std::vector< VDataSeries* >& rAllOldDataSeries =
1951 maTimeBased.m_aDataSeriesList[i];
1952 size_t m = std::min(aAllNewDataSeries.size(), rAllOldDataSeries.size());
1953 for(size_t j = 0; j < m; ++j)
1954 {
1955 aAllNewDataSeries[j]->setOldTimeBased(
1956 rAllOldDataSeries[j], (maTimeBased.nFrame % 60)/60.0);
1957 }
1958 }
1959 }
1960
1961 lcl_createLegend(
1962 LegendHelper::getLegend( mrChartModel ), mxRootShape, getComponentContext(),
1963 aParam.maRemainingSpace, rPageSize, mrChartModel, aParam.mpSeriesPlotterContainer->getLegendEntryProviderList(),
1964 lcl_getDefaultWritingModeFromPool( m_pDrawModelWrapper ) );
1965
1966 if (!bHasRelativeSize && (aParam.maRemainingSpace.Width <= 0 || aParam.maRemainingSpace.Height <= 0))
1967 return;
1968
1969 if (!createAxisTitleShapes2D(aParam, rPageSize, bHasRelativeSize))
1970 return;
1971
1972 bool bDummy = false;
1973 bool bIsVertical = xDiagram && xDiagram->getVertical(bDummy, bDummy);
1974
1975 if (getAvailablePosAndSizeForDiagram(aParam, rPageSize, xDiagram))
1976 {
1977 awt::Rectangle aUsedOuterRect = impl_createDiagramAndContent(aParam, rPageSize);
1978
1979 if (aParam.mxPlotAreaWithAxes.is())
1980 {
1981 aParam.mxPlotAreaWithAxes->setPosition(awt::Point(aUsedOuterRect.X, aUsedOuterRect.Y));
1982 aParam.mxPlotAreaWithAxes->setSize(awt::Size(aUsedOuterRect.Width, aUsedOuterRect.Height));
1983 }
1984
1985 //correct axis title position
1986 awt::Rectangle aDiagramPlusAxesRect( aUsedOuterRect );
1987 if (aParam.mbAutoPosTitleX)
1988 changePositionOfAxisTitle(aParam.mpVTitleX.get(), TitleAlignment::ALIGN_BOTTOM, aDiagramPlusAxesRect, rPageSize);
1989 if (aParam.mbAutoPosTitleY)
1990 changePositionOfAxisTitle(aParam.mpVTitleY.get(), TitleAlignment::ALIGN_LEFT, aDiagramPlusAxesRect, rPageSize);
1991 if (aParam.mbAutoPosTitleZ)
1992 changePositionOfAxisTitle(aParam.mpVTitleZ.get(), TitleAlignment::ALIGN_Z, aDiagramPlusAxesRect, rPageSize);
1993 if (aParam.mbAutoPosSecondTitleX)
1994 changePositionOfAxisTitle(aParam.mpVTitleSecondX.get(), bIsVertical? TitleAlignment::ALIGN_RIGHT : TitleAlignment::ALIGN_TOP, aDiagramPlusAxesRect, rPageSize);
1995 if (aParam.mbAutoPosSecondTitleY)
1996 changePositionOfAxisTitle(aParam.mpVTitleSecondY.get(), bIsVertical? TitleAlignment::ALIGN_TOP : TitleAlignment::ALIGN_RIGHT, aDiagramPlusAxesRect, rPageSize);
1997 }
1998
1999 //cleanup: remove all empty group shapes to avoid grey border lines:
2000 lcl_removeEmptyGroupShapes( *mxRootShape->GetSdrObject() );
2001
2002 if(maTimeBased.bTimeBased && maTimeBased.nFrame % 60 == 0)
2003 {
2004 // create copy of the data for next frame
2005 auto& rSeriesPlotter = aParam.mpSeriesPlotterContainer->getSeriesPlotterList();
2006 size_t n = rSeriesPlotter.size();
2007 maTimeBased.m_aDataSeriesList.clear();
2008 maTimeBased.m_aDataSeriesList.resize(n);
2009 for(size_t i = 0; i < n; ++i)
2010 {
2011 std::vector<VDataSeries*> aAllNewDataSeries = rSeriesPlotter[i]->getAllSeries();
2012 std::vector<VDataSeries*>& rAllOldDataSeries = maTimeBased.m_aDataSeriesList[i];
2013 size_t m = aAllNewDataSeries.size();
2014 for(size_t j = 0; j < m; ++j)
2015 {
2016 rAllOldDataSeries.push_back( aAllNewDataSeries[j]->
2017 createCopyForTimeBased() );
2018 }
2019 }
2020
2021 maTimeBased.maTimer.Stop();
2022 }
2023
2024 if(maTimeBased.bTimeBased && !maTimeBased.maTimer.IsActive())
2025 {
2026 maTimeBased.maTimer.SetTimeout(15);
2027 maTimeBased.maTimer.SetInvokeHandler(LINK(this, ChartView, UpdateTimeBased));
2028 maTimeBased.maTimer.Start();
2029 }
2030 }
2031
createAxisTitleShapes2D(CreateShapeParam2D & rParam,const css::awt::Size & rPageSize,bool bHasRelativeSize)2032 bool ChartView::createAxisTitleShapes2D( CreateShapeParam2D& rParam, const css::awt::Size& rPageSize, bool bHasRelativeSize )
2033 {
2034 rtl::Reference<Diagram> xDiagram = mrChartModel.getFirstChartDiagram();
2035
2036 rtl::Reference< ChartType > xChartType;
2037 sal_Int32 nDimension = 0;
2038 if (xDiagram)
2039 {
2040 xChartType = xDiagram->getChartTypeByIndex( 0 );
2041 nDimension = xDiagram->getDimension();
2042 }
2043
2044 if( ChartTypeHelper::isSupportingMainAxis( xChartType, nDimension, 0 ) )
2045 rParam.mpVTitleX = lcl_createTitle( TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION, mxRootShape, mrChartModel
2046 , rParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_BOTTOM, rParam.mbAutoPosTitleX );
2047 if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
2048 return false;
2049
2050 if( ChartTypeHelper::isSupportingMainAxis( xChartType, nDimension, 1 ) )
2051 rParam.mpVTitleY = lcl_createTitle( TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION, mxRootShape, mrChartModel
2052 , rParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_LEFT, rParam.mbAutoPosTitleY );
2053 if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
2054 return false;
2055
2056 if( ChartTypeHelper::isSupportingMainAxis( xChartType, nDimension, 2 ) )
2057 rParam.mpVTitleZ = lcl_createTitle( TitleHelper::Z_AXIS_TITLE, mxRootShape, mrChartModel
2058 , rParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_RIGHT, rParam.mbAutoPosTitleZ );
2059 if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
2060 return false;
2061
2062 bool bDummy = false;
2063 bool bIsVertical = xDiagram && xDiagram->getVertical( bDummy, bDummy );
2064
2065 if( ChartTypeHelper::isSupportingSecondaryAxis( xChartType, nDimension ) )
2066 rParam.mpVTitleSecondX = lcl_createTitle( TitleHelper::SECONDARY_X_AXIS_TITLE, mxRootShape, mrChartModel
2067 , rParam.maRemainingSpace, rPageSize, bIsVertical? TitleAlignment::ALIGN_RIGHT : TitleAlignment::ALIGN_TOP, rParam.mbAutoPosSecondTitleX );
2068 if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
2069 return false;
2070
2071 if( ChartTypeHelper::isSupportingSecondaryAxis( xChartType, nDimension ) )
2072 rParam.mpVTitleSecondY = lcl_createTitle( TitleHelper::SECONDARY_Y_AXIS_TITLE, mxRootShape, mrChartModel
2073 , rParam.maRemainingSpace, rPageSize, bIsVertical? TitleAlignment::ALIGN_TOP : TitleAlignment::ALIGN_RIGHT, rParam.mbAutoPosSecondTitleY );
2074 if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
2075 return false;
2076
2077 return true;
2078 }
2079
2080 } //namespace chart
2081
2082 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_chart2_ChartView_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)2083 com_sun_star_comp_chart2_ChartView_get_implementation(css::uno::XComponentContext *context,
2084 css::uno::Sequence<css::uno::Any> const &)
2085 {
2086 rtl::Reference<::chart::ChartModel> pChartModel = new ::chart::ChartModel(context);
2087 return cppu::acquire(new ::chart::ChartView(context, *pChartModel));
2088 }
2089
2090 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2091