xref: /core/chart2/source/view/main/ChartView.cxx (revision 64da52a9)
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