/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <config_features.h>
#include <ChartView.hxx>
#include <chartview/DrawModelWrapper.hxx>
#include <NumberFormatterWrapper.hxx>
#include <ViewDefines.hxx>
#include <VDiagram.hxx>
#include "VTitle.hxx"
#include "VButton.hxx"
#include <ShapeFactory.hxx>
#include <VCoordinateSystem.hxx>
#include <VSeriesPlotter.hxx>
#include <CommonConverters.hxx>
#include <TitleHelper.hxx>
#include <LegendHelper.hxx>
#include "VLegend.hxx"
#include <PropertyMapper.hxx>
#include <ChartModel.hxx>
#include <ChartModelHelper.hxx>
#include <ChartTypeHelper.hxx>
#include <ScaleAutomatism.hxx>
#include <MinimumAndMaximumSupplier.hxx>
#include <ObjectIdentifier.hxx>
#include <DiagramHelper.hxx>
#include <RelativePositionHelper.hxx>
#include <servicenames.hxx>
#include <AxisHelper.hxx>
#include <AxisIndexDefines.hxx>
#include <ControllerLockGuard.hxx>
#include <BaseGFXHelper.hxx>
#include <DataSeriesHelper.hxx>
#include <DateHelper.hxx>
#include <ExplicitCategoriesProvider.hxx>
#include <defines.hxx>
#include <unonames.hxx>
#include <editeng/frmdiritem.hxx>
#include <rtl/uuid.h>
#include <tools/globname.hxx>
#include <comphelper/fileformat.h>
#include <comphelper/scopeguard.hxx>
#include <comphelper/servicehelper.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <unotools/streamwrap.hxx>
#include <unotools/localedatawrapper.hxx>
#include <svx/charthelper.hxx>
#include <svx/svdpage.hxx>
#include <svx/unopage.hxx>
#include <svx/unoshape.hxx>
#include <vcl/svapp.hxx>
#include <osl/mutex.hxx>
#include <svx/unofill.hxx>
#include <drawinglayer/XShapeDumper.hxx>
#include <time.h>
#include <com/sun/star/awt/Size.hpp>
#include <com/sun/star/awt/Point.hpp>
#include <com/sun/star/chart/ChartAxisPosition.hpp>
#include <com/sun/star/chart/DataLabelPlacement.hpp>
#include <com/sun/star/chart/TimeUnit.hpp>
#include <com/sun/star/chart/MissingValueTreatment.hpp>
#include <com/sun/star/chart2/AxisType.hpp>
#include <com/sun/star/chart2/StackingDirection.hpp>
#include <com/sun/star/chart2/XChartDocument.hpp>
#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
#include <com/sun/star/chart2/XChartTypeContainer.hpp>
#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
#include <com/sun/star/chart2/XTitled.hpp>
#include <com/sun/star/chart2/RelativePosition.hpp>
#include <com/sun/star/chart2/RelativeSize.hpp>
#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
#include <com/sun/star/chart2/data/PivotTableFieldEntry.hpp>
#include <com/sun/star/drawing/FillStyle.hpp>
#include <com/sun/star/drawing/GraphicExportFilter.hpp>
#include <com/sun/star/drawing/LineStyle.hpp>
#include <com/sun/star/drawing/XShapeGroup.hpp>
#include <com/sun/star/drawing/XShapeDescriptor.hpp>
#include <com/sun/star/document/XExporter.hpp>
#include <com/sun/star/document/XFilter.hpp>
#include <com/sun/star/embed/Aspects.hpp>
#include <com/sun/star/io/XSeekable.hpp>
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
#include <com/sun/star/util/XModifiable.hpp>
#include <com/sun/star/util/XRefreshable.hpp>
#include <com/sun/star/util/NumberFormat.hpp>
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <com/sun/star/text/XText.hpp>
#include <com/sun/star/text/XTextDocument.hpp>
#include <com/sun/star/text/WritingMode2.hpp>
#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp>
#include <com/sun/star/view/XSelectionSupplier.hpp>
#include <svl/itempool.hxx>
#include <svl/languageoptions.hxx>
#include <comphelper/classids.hxx>
#include <servicenames_charttypes.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <osl/conditn.hxx>
#include <o3tl/make_unique.hxx>
#include <tools/diagnose_ex.h>
#include <memory>
namespace chart {
using namespace ::com::sun::star;
using namespace ::com::sun::star::chart2;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::uno::Any;
namespace {
class theExplicitValueProviderUnoTunnelId : public rtl::Static<UnoTunnelIdInit, theExplicitValueProviderUnoTunnelId> {};
typedef std::pair< sal_Int32, sal_Int32 > tFullAxisIndex; //first index is the dimension, second index is the axis index that indicates whether this is a main or secondary axis
typedef std::map< VCoordinateSystem*, tFullAxisIndex > tCoordinateSystemMap;
/** This class handles a collection of coordinate systems and is used for
* executing some action on all coordinate systems such as
* `prepareAutomaticAxisScaling` and `setExplicitScaleAndIncrement`.
* Moreover it contains the `aAutoScaling` object that is an instance of
* the `ScaleAutomatism` class. The initialization of `aAutoScaling` is
* performed in the `SeriesPlotterContainer::initAxisUsageList` method and is
* used in the `SeriesPlotterContainer::doAutoScaling` for calculating explicit
* scale and increment objects (see `SeriesPlotterContainer::doAutoScaling`).
*/
struct AxisUsage
{
AxisUsage();
~AxisUsage();
void addCoordinateSystem( VCoordinateSystem* pCooSys, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex );
std::vector< VCoordinateSystem* > getCoordinateSystems( sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex );
sal_Int32 getMaxAxisIndexForDimension( sal_Int32 nDimensionIndex );
void prepareAutomaticAxisScaling( ScaleAutomatism& rScaleAutomatism, sal_Int32 nDimIndex, sal_Int32 nAxisIndex );
void setExplicitScaleAndIncrement( sal_Int32 nDimIndex, sal_Int32 nAxisIndex, const ExplicitScaleData& rScale, const ExplicitIncrementData& rInc );
ScaleAutomatism aAutoScaling;
private:
tCoordinateSystemMap aCoordinateSystems;
std::map< sal_Int32, sal_Int32 > aMaxIndexPerDimension;
};
AxisUsage::AxisUsage()
: aAutoScaling(AxisHelper::createDefaultScale(), Date(Date::SYSTEM))
{
}
AxisUsage::~AxisUsage()
{
aCoordinateSystems.clear();
}
void AxisUsage::addCoordinateSystem( VCoordinateSystem* pCooSys, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex )
{
if(!pCooSys)
return;
tFullAxisIndex aFullAxisIndex( nDimensionIndex, nAxisIndex );
tCoordinateSystemMap::const_iterator aFound( aCoordinateSystems.find(pCooSys) );
//use one scale only once for each coordinate system
//main axis are preferred over secondary axis
//value scales are preferred
if(aFound!=aCoordinateSystems.end())
{
sal_Int32 nFoundAxisIndex = aFound->second.second;
if( nFoundAxisIndex < nAxisIndex )
return;
sal_Int32 nFoundDimension = aFound->second.first;
if( nFoundDimension ==1 )
return;
if( nFoundDimension < nDimensionIndex )
return;
}
aCoordinateSystems[pCooSys] = aFullAxisIndex;
//set maximum scale index
std::map< sal_Int32, sal_Int32 >::const_iterator aIter = aMaxIndexPerDimension.find(nDimensionIndex);
if( aIter != aMaxIndexPerDimension.end() )
{
sal_Int32 nCurrentMaxIndex = aIter->second;
if( nCurrentMaxIndex < nAxisIndex )
aMaxIndexPerDimension[nDimensionIndex]=nAxisIndex;
}
else
aMaxIndexPerDimension[nDimensionIndex]=nAxisIndex;
}
std::vector< VCoordinateSystem* > AxisUsage::getCoordinateSystems( sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex )
{
std::vector< VCoordinateSystem* > aRet;
for (auto const& coordinateSystem : aCoordinateSystems)
{
if( coordinateSystem.second.first != nDimensionIndex )
continue;
if( coordinateSystem.second.second != nAxisIndex )
continue;
aRet.push_back( coordinateSystem.first );
}
return aRet;
}
sal_Int32 AxisUsage::getMaxAxisIndexForDimension( sal_Int32 nDimensionIndex )
{
sal_Int32 nRet = -1;
std::map< sal_Int32, sal_Int32 >::const_iterator aIter = aMaxIndexPerDimension.find(nDimensionIndex);
if( aIter != aMaxIndexPerDimension.end() )
nRet = aIter->second;
return nRet;
}
void AxisUsage::prepareAutomaticAxisScaling( ScaleAutomatism& rScaleAutomatism, sal_Int32 nDimIndex, sal_Int32 nAxisIndex )
{
std::vector<VCoordinateSystem*> aVCooSysList = getCoordinateSystems(nDimIndex, nAxisIndex);
for (VCoordinateSystem * i : aVCooSysList)
i->prepareAutomaticAxisScaling(rScaleAutomatism, nDimIndex, nAxisIndex);
}
void AxisUsage::setExplicitScaleAndIncrement(
sal_Int32 nDimIndex, sal_Int32 nAxisIndex, const ExplicitScaleData& rScale, const ExplicitIncrementData& rInc )
{
std::vector<VCoordinateSystem*> aVCooSysList = getCoordinateSystems(nDimIndex, nAxisIndex);
for (VCoordinateSystem* i : aVCooSysList)
i->setExplicitScaleAndIncrement(nDimIndex, nAxisIndex, rScale, rInc);
}
typedef std::vector<std::unique_ptr<VSeriesPlotter> > SeriesPlottersType;
/** This class is a container of `SeriesPlotter` objects (such as `PieChart`
* instances). It is used for initializing coordinate systems, axes and scales
* of all series plotters which belongs to the container.
*/
class SeriesPlotterContainer
{
public:
explicit SeriesPlotterContainer( std::vector< std::unique_ptr<VCoordinateSystem> >& rVCooSysList );
~SeriesPlotterContainer();
/** It is used to set coordinate systems (`m_rVCooSysList`), this method
* is invoked by `ChartView::createShapes2D` before of
* `ChartView::impl_createDiagramAndContent`.
* Coordinate systems are retrieved through the `XCoordinateSystemContainer`
* interface implemented by a diagram object which is provided by the
* `ChartModel` object passed to the method (`rChartModel.getFirstDiagram()`).
*
* It is used for creating series plotters and appending them
* to `m_aSeriesPlotterList`. The created series plotters are initialized
* through data (number formats supplier, color scheme, data series),
* extracted from the chart model or the diagram objects. An exception is
* the explicit category provider that is retrieved through the
* `VCoordinateSystem` object used by the series plotter.
*
* It sets the minimum-maximum supplier for a coordinate system:
* this supplier is the series plotter itself which utilizes the given
* coordinate system. In fact `VSeriesPlotter` has `MinimumMaximumSupplier`
* as one of its base classes.
* Hence, for instance, a `PieChart`, which is a series plotter, is
* a `MinimumMaximumSupplier`, too.
*/
void initializeCooSysAndSeriesPlotter( ChartModel& rModel );
/** This method is invoked by `ChartView::impl_createDiagramAndContent`.
* It iterates on every axis of every coordinate systems, and if the axis
* is not yet present in `m_aAxisUsageList` it creates a new `AxisUsage`
* object and initialize its `aAutoScaling` member to the `ScaleData`
* object of the current axis.
*/
void initAxisUsageList(const Date& rNullDate);
/**
* Perform automatic axis scaling and determine the amount and spacing of
* increments. It assumes that the caller has determined the size of the
* largest axis label text object prior to calling this method.
*
* The new axis scaling data will be stored in the VCoordinateSystem
* objects. The label alignment direction for each axis will also get
* determined during this process, and stored in VAxis.
*
* This method is invoked by `ChartView::impl_createDiagramAndContent`
* soon after `initAxisUsageList`.
* It initializes explicit scale and increment objects for all coordinate
* systems in `m_rVCooSysList`.
* This action is achieved by iterating on the `m_aAxisUsageList` container,
* and performing 3 steps:
* 1- call `VCoordinateSystem::prepareAutomaticAxisScaling` for setting
* scaling parameters of the `aAutoScaling` member (a `ScaleAutomatism`
* object) for the current `AxisUsage` instance
* (see `VCoordinateSystem::prepareAutomaticAxisScaling`);
* 2- calculate the explicit scale and increment objects
* (see ScaleAutomatism::calculateExplicitScaleAndIncrement);
* 3- set the explicit scale and increment objects for each coordinate
* system.
*/
void doAutoScaling( ChartModel& rModel );
/**
* After auto-scaling is performed, call this method to set the explicit
* scaling and increment data to all relevant VAxis objects.
*/
void updateScalesAndIncrementsOnAxes();
/**
* After auto-scaling is performed, call this method to set the explicit
* scaling data to all the plotters.
*/
void setScalesFromCooSysToPlotter();
void setNumberFormatsFromAxes();
drawing::Direction3D getPreferredAspectRatio();
SeriesPlottersType& getSeriesPlotterList() { return m_aSeriesPlotterList; }
std::vector< std::unique_ptr<VCoordinateSystem> >& getCooSysList() { return m_rVCooSysList; }
std::vector< LegendEntryProvider* > getLegendEntryProviderList();
void AdaptScaleOfYAxisWithoutAttachedSeries( ChartModel& rModel );
bool isCategoryPositionShifted(
const chart2::ScaleData& rSourceScale, bool bHasComplexCategories ) const;
private:
/** A vector of series plotters.
*/
SeriesPlottersType m_aSeriesPlotterList;
/** A vector of coordinate systems.
*/
std::vector< std::unique_ptr<VCoordinateSystem> >& m_rVCooSysList;
/** A map whose key is a `XAxis` interface and the related value is
* an object of `AxisUsage` type.
*/
std::map< uno::Reference< XAxis >, AxisUsage > m_aAxisUsageList;
/**
* Max axis index of all dimensions. Currently this can be either 0 or 1
* since we only support primary and secondary axes per dimension. The
* value of 0 means all dimensions have only primary axis, while 1 means
* at least one dimension has a secondary axis.
*/
sal_Int32 m_nMaxAxisIndex;
bool m_bChartTypeUsesShiftedCategoryPositionPerDefault;
sal_Int32 m_nDefaultDateNumberFormat;
};
SeriesPlotterContainer::SeriesPlotterContainer( std::vector< std::unique_ptr<VCoordinateSystem> >& rVCooSysList )
: m_rVCooSysList( rVCooSysList )
, m_nMaxAxisIndex(0)
, m_bChartTypeUsesShiftedCategoryPositionPerDefault(false)
, m_nDefaultDateNumberFormat(0)
{
}
SeriesPlotterContainer::~SeriesPlotterContainer()
{
// - remove plotter from coordinatesystems
for(auto & nC : m_rVCooSysList)
nC->clearMinimumAndMaximumSupplierList();
}
std::vector< LegendEntryProvider* > SeriesPlotterContainer::getLegendEntryProviderList()
{
std::vector< LegendEntryProvider* > aRet( m_aSeriesPlotterList.size() );
sal_Int32 nN = 0;
for( std::unique_ptr<VSeriesPlotter>& aPlotter : m_aSeriesPlotterList)
aRet[nN++] = aPlotter.get();
return aRet;
}
VCoordinateSystem* findInCooSysList( const std::vector< std::unique_ptr<VCoordinateSystem> >& rVCooSysList
, const uno::Reference< XCoordinateSystem >& xCooSys )
{
for(auto & pVCooSys : rVCooSysList)
{
if(pVCooSys->getModel()==xCooSys)
return pVCooSys.get();
}
return nullptr;
}
VCoordinateSystem* lcl_getCooSysForPlotter( const std::vector< std::unique_ptr<VCoordinateSystem> >& rVCooSysList, MinimumAndMaximumSupplier* pMinimumAndMaximumSupplier )
{
if(!pMinimumAndMaximumSupplier)
return nullptr;
for(auto & pVCooSys : rVCooSysList)
{
if(pVCooSys->hasMinimumAndMaximumSupplier( pMinimumAndMaximumSupplier ))
return pVCooSys.get();
}
return nullptr;
}
VCoordinateSystem* addCooSysToList( std::vector< std::unique_ptr<VCoordinateSystem> >& rVCooSysList
, const uno::Reference< XCoordinateSystem >& xCooSys
, ChartModel& rChartModel )
{
VCoordinateSystem* pExistingVCooSys = findInCooSysList( rVCooSysList, xCooSys );
if( pExistingVCooSys )
return pExistingVCooSys;
std::unique_ptr<VCoordinateSystem> pVCooSys = VCoordinateSystem::createCoordinateSystem(xCooSys );
if(!pVCooSys)
return nullptr;
OUString aCooSysParticle( ObjectIdentifier::createParticleForCoordinateSystem( xCooSys, rChartModel ) );
pVCooSys->setParticle(aCooSysParticle);
pVCooSys->setExplicitCategoriesProvider( new ExplicitCategoriesProvider(xCooSys, rChartModel) );
rVCooSysList.push_back( std::move(pVCooSys) );
return rVCooSysList.back().get();
}
void SeriesPlotterContainer::initializeCooSysAndSeriesPlotter(
ChartModel& rChartModel )
{
uno::Reference< XDiagram > xDiagram( rChartModel.getFirstDiagram() );
if( !xDiagram.is())
return;
uno::Reference< util::XNumberFormatsSupplier > xNumberFormatsSupplier( static_cast< ::cppu::OWeakObject* >( &rChartModel ), uno::UNO_QUERY );
if( rChartModel.hasInternalDataProvider() && DiagramHelper::isSupportingDateAxis( xDiagram ) )
m_nDefaultDateNumberFormat=DiagramHelper::getDateNumberFormat( xNumberFormatsSupplier );
sal_Int32 nDimensionCount = DiagramHelper::getDimension( xDiagram );
if(!nDimensionCount)
{
//@todo handle mixed dimension
nDimensionCount = 2;
}
bool bSortByXValues = false;
bool bConnectBars = false;
bool bGroupBarsPerAxis = true;
bool bIncludeHiddenCells = true;
sal_Int32 nStartingAngle = 90;
sal_Int32 n3DRelativeHeight = 100;
try
{
uno::Reference< beans::XPropertySet > xDiaProp( xDiagram, uno::UNO_QUERY_THROW );
xDiaProp->getPropertyValue(CHART_UNONAME_SORT_BY_XVALUES) >>= bSortByXValues;
xDiaProp->getPropertyValue( "ConnectBars" ) >>= bConnectBars;
xDiaProp->getPropertyValue( "GroupBarsPerAxis" ) >>= bGroupBarsPerAxis;
xDiaProp->getPropertyValue( "IncludeHiddenCells" ) >>= bIncludeHiddenCells;
xDiaProp->getPropertyValue( "StartingAngle" ) >>= nStartingAngle;
if (nDimensionCount == 3)
{
xDiaProp->getPropertyValue( "3DRelativeHeight" ) >>= n3DRelativeHeight;
}
}
catch( const uno::Exception & )
{
DBG_UNHANDLED_EXCEPTION("chart2" );
}
//prepare for autoscaling and shape creation
// - create plotter for charttypes (for each first scale group at each plotter, as they are independent)
// - add series to plotter (thus each charttype can provide minimum and maximum values for autoscaling)
// - add plotter to coordinate systems
//iterate through all coordinate systems
uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );
OSL_ASSERT( xCooSysContainer.is());
if( !xCooSysContainer.is())
return;
uno::Reference< XColorScheme > xColorScheme( xDiagram->getDefaultColorScheme());
uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
sal_Int32 nGlobalSeriesIndex = 0;//for automatic symbols
for( sal_Int32 nCS = 0; nCS < aCooSysList.getLength(); ++nCS )
{
uno::Reference< XCoordinateSystem > xCooSys( aCooSysList[nCS] );
VCoordinateSystem* pVCooSys = addCooSysToList(m_rVCooSysList,xCooSys,rChartModel);
//iterate through all chart types in the current coordinate system
uno::Reference< XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY );
OSL_ASSERT( xChartTypeContainer.is());
if( !xChartTypeContainer.is() )
continue;
uno::Sequence< uno::Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
for( sal_Int32 nT = 0; nT < aChartTypeList.getLength(); ++nT )
{
uno::Reference< XChartType > xChartType( aChartTypeList[nT] );
if(nDimensionCount == 3 && xChartType->getChartType().equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_PIE))
{
uno::Reference< beans::XPropertySet > xPropertySet( xChartType, uno::UNO_QUERY );
if (xPropertySet.is())
{
try
{
sal_Int32 n3DRelativeHeightOldValue(100);
uno::Any aAny = xPropertySet->getPropertyValue( "3DRelativeHeight" );
aAny >>= n3DRelativeHeightOldValue;
if (n3DRelativeHeightOldValue != n3DRelativeHeight)
xPropertySet->setPropertyValue( "3DRelativeHeight", uno::Any(n3DRelativeHeight) );
}
catch (const uno::Exception&) { }
}
}
if(nT==0)
m_bChartTypeUsesShiftedCategoryPositionPerDefault = ChartTypeHelper::shiftCategoryPosAtXAxisPerDefault( xChartType );
bool bExcludingPositioning = DiagramHelper::getDiagramPositioningMode( xDiagram ) == DiagramPositioningMode_EXCLUDING;
VSeriesPlotter* pPlotter = VSeriesPlotter::createSeriesPlotter( xChartType, nDimensionCount, bExcludingPositioning );
if( !pPlotter )
continue;
m_aSeriesPlotterList.push_back( std::unique_ptr<VSeriesPlotter>(pPlotter) );
pPlotter->setNumberFormatsSupplier( xNumberFormatsSupplier );
pPlotter->setColorScheme( xColorScheme );
if(pVCooSys)
pPlotter->setExplicitCategoriesProvider( pVCooSys->getExplicitCategoriesProvider() );
sal_Int32 nMissingValueTreatment = DiagramHelper::getCorrectedMissingValueTreatment( xDiagram, xChartType );
if(pVCooSys)
pVCooSys->addMinimumAndMaximumSupplier(pPlotter);
uno::Reference< XDataSeriesContainer > xDataSeriesContainer( xChartType, uno::UNO_QUERY );
OSL_ASSERT( xDataSeriesContainer.is());
if( !xDataSeriesContainer.is() )
continue;
sal_Int32 zSlot=-1;
sal_Int32 xSlot=-1;
sal_Int32 ySlot=-1;
uno::Sequence< uno::Reference< XDataSeries > > aSeriesList( xDataSeriesContainer->getDataSeries() );
for( sal_Int32 nS = 0; nS < aSeriesList.getLength(); ++nS )
{
uno::Reference< XDataSeries > xDataSeries( aSeriesList[nS], uno::UNO_QUERY );
if(!xDataSeries.is())
continue;
if( !bIncludeHiddenCells && !DataSeriesHelper::hasUnhiddenData(xDataSeries) )
continue;
std::unique_ptr<VDataSeries> pSeries(new VDataSeries( xDataSeries ));
pSeries->setGlobalSeriesIndex(nGlobalSeriesIndex);
nGlobalSeriesIndex++;
if( bSortByXValues )
pSeries->doSortByXValues();
pSeries->setConnectBars( bConnectBars );
pSeries->setGroupBarsPerAxis( bGroupBarsPerAxis );
pSeries->setStartingAngle( nStartingAngle );
pSeries->setMissingValueTreatment( nMissingValueTreatment );
OUString aSeriesParticle( ObjectIdentifier::createParticleForSeries( 0, nCS, nT, nS ) );
pSeries->setParticle(aSeriesParticle);
OUString aRole( ChartTypeHelper::getRoleOfSequenceForDataLabelNumberFormatDetection( xChartType ) );
pSeries->setRoleOfSequenceForDataLabelNumberFormatDetection(aRole);
//ignore secondary axis for charttypes that do not support them
if( pSeries->getAttachedAxisIndex() != MAIN_AXIS_INDEX &&
!ChartTypeHelper::isSupportingSecondaryAxis( xChartType, nDimensionCount ) )
{
pSeries->setAttachedAxisIndex(MAIN_AXIS_INDEX);
}
StackingDirection eDirection = pSeries->getStackingDirection();
switch(eDirection)
{
case StackingDirection_NO_STACKING:
xSlot++; ySlot=-1;
if(zSlot<0)
zSlot=0;
break;
case StackingDirection_Y_STACKING:
ySlot++;
if(xSlot<0)
xSlot=0;
if(zSlot<0)
zSlot=0;
break;
case StackingDirection_Z_STACKING:
zSlot++; xSlot=-1; ySlot=-1;
break;
default:
// UNO enums have one additional auto-generated case
break;
}
pPlotter->addSeries( std::move(pSeries), zSlot, xSlot, ySlot );
}
}
}
//transport seriesnames to the coordinatesystems if needed
if( !m_aSeriesPlotterList.empty() )
{
uno::Sequence< OUString > aSeriesNames;
bool bSeriesNamesInitialized = false;
for(auto & pVCooSys : m_rVCooSysList)
{
if( pVCooSys->needSeriesNamesForAxis() )
{
if(!bSeriesNamesInitialized)
{
aSeriesNames = m_aSeriesPlotterList[0]->getSeriesNames();
bSeriesNamesInitialized = true;
}
pVCooSys->setSeriesNamesForAxis( aSeriesNames );
}
}
}
}
bool SeriesPlotterContainer::isCategoryPositionShifted(
const chart2::ScaleData& rSourceScale, bool bHasComplexCategories ) const
{
if (rSourceScale.AxisType == AxisType::CATEGORY && m_bChartTypeUsesShiftedCategoryPositionPerDefault)
return true;
if (rSourceScale.AxisType==AxisType::CATEGORY && bHasComplexCategories)
return true;
if (rSourceScale.AxisType == AxisType::DATE)
return true;
if (rSourceScale.AxisType == AxisType::SERIES)
return true;
return false;
}
void SeriesPlotterContainer::initAxisUsageList(const Date& rNullDate)
{
m_aAxisUsageList.clear();
// Loop through coordinate systems in the diagram (though for now
// there should only be one coordinate system per diagram).
for (auto & pVCooSys : m_rVCooSysList)
{
uno::Reference<XCoordinateSystem> xCooSys = pVCooSys->getModel();
sal_Int32 nDimCount = xCooSys->getDimension();
for (sal_Int32 nDimIndex = 0; nDimIndex < nDimCount; ++nDimIndex)
{
bool bDateAxisAllowed = ChartTypeHelper::isSupportingDateAxis(
AxisHelper::getChartTypeByIndex(xCooSys, 0), nDimIndex);
// Each dimension may have primary and secondary axes.
const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nDimIndex);
for (sal_Int32 nAxisIndex = 0; nAxisIndex <= nMaxAxisIndex; ++nAxisIndex)
{
uno::Reference<XAxis> xAxis = xCooSys->getAxisByDimension(nDimIndex, nAxisIndex);
if (!xAxis.is())
continue;
if (m_aAxisUsageList.find(xAxis) == m_aAxisUsageList.end())
{
// Create axis usage object for this axis.
chart2::ScaleData aSourceScale = xAxis->getScaleData();
ExplicitCategoriesProvider* pCatProvider = pVCooSys->getExplicitCategoriesProvider();
if (nDimIndex == 0)
AxisHelper::checkDateAxis( aSourceScale, pCatProvider, bDateAxisAllowed );
bool bHasComplexCat = pCatProvider && pCatProvider->hasComplexCategories();
aSourceScale.ShiftedCategoryPosition = isCategoryPositionShifted(aSourceScale, bHasComplexCat);
m_aAxisUsageList[xAxis].aAutoScaling = ScaleAutomatism(aSourceScale, rNullDate);
}
AxisUsage& rAxisUsage = m_aAxisUsageList[xAxis];
rAxisUsage.addCoordinateSystem(pVCooSys.get(), nDimIndex, nAxisIndex);
}
}
}
// Determine the highest axis index of all dimensions.
m_nMaxAxisIndex = 0;
for (auto & pVCooSys : m_rVCooSysList)
{
uno::Reference<XCoordinateSystem> xCooSys = pVCooSys->getModel();
sal_Int32 nDimCount = xCooSys->getDimension();
for (sal_Int32 nDimIndex = 0; nDimIndex < nDimCount; ++nDimIndex)
{
for (auto & axisUsage : m_aAxisUsageList)
{
sal_Int32 nLocalMax = axisUsage.second.getMaxAxisIndexForDimension(nDimIndex);
if (m_nMaxAxisIndex < nLocalMax)
m_nMaxAxisIndex = nLocalMax;
}
}
}
}
void SeriesPlotterContainer::setScalesFromCooSysToPlotter()
{
//set scales to plotter to enable them to provide the preferred scene AspectRatio
for( std::unique_ptr<VSeriesPlotter>& aPlotter : m_aSeriesPlotterList )
{
VSeriesPlotter* pSeriesPlotter = aPlotter.get();
VCoordinateSystem* pVCooSys = lcl_getCooSysForPlotter( m_rVCooSysList, pSeriesPlotter );
if(pVCooSys)
{
pSeriesPlotter->setScales( pVCooSys->getExplicitScales(0,0), pVCooSys->getPropertySwapXAndYAxis() );
sal_Int32 nMaxAxisIndex = pVCooSys->getMaximumAxisIndexByDimension(1);//only additional value axis are relevant for series plotter
for( sal_Int32 nI=1; nI<=nMaxAxisIndex; nI++ )
pSeriesPlotter->addSecondaryValueScale( pVCooSys->getExplicitScale(1,nI), nI );
}
}
}
void SeriesPlotterContainer::setNumberFormatsFromAxes()
{
//set numberformats to plotter to enable them to display the data labels in the numberformat of the axis
for( std::unique_ptr<VSeriesPlotter>& aPlotter : m_aSeriesPlotterList )
{
VSeriesPlotter* pSeriesPlotter = aPlotter.get();
VCoordinateSystem* pVCooSys = lcl_getCooSysForPlotter( m_rVCooSysList, pSeriesPlotter );
if(pVCooSys)
{
AxesNumberFormats aAxesNumberFormats;
uno::Reference< XCoordinateSystem > xCooSys = pVCooSys->getModel();
sal_Int32 nDimensionCount = xCooSys->getDimension();
for(sal_Int32 nDimensionIndex=0; nDimensionIndex<nDimensionCount; ++nDimensionIndex)
{
const sal_Int32 nMaximumAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex);
for(sal_Int32 nAxisIndex=0; nAxisIndex<=nMaximumAxisIndex; ++nAxisIndex)
{
try
{
Reference< beans::XPropertySet > xAxisProp( xCooSys->getAxisByDimension( nDimensionIndex, nAxisIndex ), uno::UNO_QUERY );
if( xAxisProp.is())
{
sal_Int32 nNumberFormatKey(0);
if( xAxisProp->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nNumberFormatKey )
{
aAxesNumberFormats.setFormat( nNumberFormatKey, nDimensionIndex, nAxisIndex );
}
else if( nDimensionIndex==0 )
{
//provide a default date format for date axis with own data
aAxesNumberFormats.setFormat( m_nDefaultDateNumberFormat, nDimensionIndex, nAxisIndex );
}
}
}
catch( const lang::IndexOutOfBoundsException& e )
{
SAL_WARN("chart2", "Exception caught. " << e );
}
}
}
pSeriesPlotter->setAxesNumberFormats( aAxesNumberFormats );
}
}
}
void SeriesPlotterContainer::updateScalesAndIncrementsOnAxes()
{
for(auto & nC : m_rVCooSysList)
nC->updateScalesAndIncrementsOnAxes();
}
void SeriesPlotterContainer::doAutoScaling( ChartModel& rChartModel )
{
if (m_aSeriesPlotterList.empty() || m_aAxisUsageList.empty())
// We need these two containers populated to do auto-scaling. Bail out.
return;
//iterate over the main scales first than secondary axis
for (sal_Int32 nAxisIndex = 0; nAxisIndex <= m_nMaxAxisIndex; ++nAxisIndex)
{
// - first do autoscale for all x and z scales (because they are treated independent)
for (auto & axisUsage : m_aAxisUsageList)
{
AxisUsage& rAxisUsage = axisUsage.second;
rAxisUsage.prepareAutomaticAxisScaling(rAxisUsage.aAutoScaling, 0, nAxisIndex);
rAxisUsage.prepareAutomaticAxisScaling(rAxisUsage.aAutoScaling, 2, nAxisIndex);
ExplicitScaleData aExplicitScale;
ExplicitIncrementData aExplicitIncrement;
rAxisUsage.aAutoScaling.calculateExplicitScaleAndIncrement( aExplicitScale, aExplicitIncrement );
rAxisUsage.setExplicitScaleAndIncrement(0, nAxisIndex, aExplicitScale, aExplicitIncrement);
rAxisUsage.setExplicitScaleAndIncrement(2, nAxisIndex, aExplicitScale, aExplicitIncrement);
}
// - second do autoscale for the dependent y scales (the coordinate systems are prepared with x and z scales already )
for (auto & axisUsage : m_aAxisUsageList)
{
AxisUsage& rAxisUsage = axisUsage.second;
rAxisUsage.prepareAutomaticAxisScaling(rAxisUsage.aAutoScaling, 1, nAxisIndex);
ExplicitScaleData aExplicitScale;
ExplicitIncrementData aExplicitIncrement;
rAxisUsage.aAutoScaling.calculateExplicitScaleAndIncrement( aExplicitScale, aExplicitIncrement );
rAxisUsage.setExplicitScaleAndIncrement(0, nAxisIndex, aExplicitScale, aExplicitIncrement);
rAxisUsage.setExplicitScaleAndIncrement(1, nAxisIndex, aExplicitScale, aExplicitIncrement);
rAxisUsage.setExplicitScaleAndIncrement(2, nAxisIndex, aExplicitScale, aExplicitIncrement);
}
}
AdaptScaleOfYAxisWithoutAttachedSeries( rChartModel );
}
void SeriesPlotterContainer::AdaptScaleOfYAxisWithoutAttachedSeries( ChartModel& rModel )
{
//issue #i80518#
for( sal_Int32 nAxisIndex=0; nAxisIndex<=m_nMaxAxisIndex; nAxisIndex++ )
{
for (auto & axisUsage : m_aAxisUsageList)
{
AxisUsage& rAxisUsage = axisUsage.second;
std::vector< VCoordinateSystem* > aVCooSysList_Y = rAxisUsage.getCoordinateSystems( 1, nAxisIndex );
if( aVCooSysList_Y.empty() )
continue;
uno::Reference< XDiagram > xDiagram( rModel.getFirstDiagram() );
if (!xDiagram.is())
continue;
bool bSeriesAttachedToThisAxis = false;
sal_Int32 nAttachedAxisIndex = -1;
{
std::vector< Reference< XDataSeries > > aSeriesVector( DiagramHelper::getDataSeriesFromDiagram( xDiagram ) );
for (auto const& series : aSeriesVector)
{
sal_Int32 nCurrentIndex = DataSeriesHelper::getAttachedAxisIndex(series);
if( nAxisIndex == nCurrentIndex )
{
bSeriesAttachedToThisAxis = true;
break;
}
else if( nAttachedAxisIndex<0 || nAttachedAxisIndex>nCurrentIndex )
nAttachedAxisIndex=nCurrentIndex;
}
}
if (bSeriesAttachedToThisAxis || nAttachedAxisIndex < 0)
continue;
for(VCoordinateSystem* nC : aVCooSysList_Y)
{
nC->prepareAutomaticAxisScaling( rAxisUsage.aAutoScaling, 1, nAttachedAxisIndex );
ExplicitScaleData aExplicitScaleSource = nC->getExplicitScale( 1,nAttachedAxisIndex );
ExplicitIncrementData aExplicitIncrementSource = nC->getExplicitIncrement( 1,nAttachedAxisIndex );
ExplicitScaleData aExplicitScaleDest = nC->getExplicitScale( 1,nAxisIndex );
ExplicitIncrementData aExplicitIncrementDest = nC->getExplicitIncrement( 1,nAxisIndex );
aExplicitScaleDest.Orientation = aExplicitScaleSource.Orientation;
aExplicitScaleDest.Scaling = aExplicitScaleSource.Scaling;
aExplicitScaleDest.AxisType = aExplicitScaleSource.AxisType;
aExplicitIncrementDest.BaseValue = aExplicitIncrementSource.BaseValue;
ScaleData aScale( rAxisUsage.aAutoScaling.getScale() );
if( !aScale.Minimum.hasValue() )
{
bool bNewMinOK = true;
double fMax=0.0;
if( aScale.Maximum >>= fMax )
bNewMinOK = (aExplicitScaleSource.Minimum <= fMax);
if( bNewMinOK )
aExplicitScaleDest.Minimum = aExplicitScaleSource.Minimum;
}
else
aExplicitIncrementDest.BaseValue = aExplicitScaleDest.Minimum;
if( !aScale.Maximum.hasValue() )
{
bool bNewMaxOK = true;
double fMin=0.0;
if( aScale.Minimum >>= fMin )
bNewMaxOK = (fMin <= aExplicitScaleSource.Maximum);
if( bNewMaxOK )
aExplicitScaleDest.Maximum = aExplicitScaleSource.Maximum;
}
if( !aScale.Origin.hasValue() )
aExplicitScaleDest.Origin = aExplicitScaleSource.Origin;
if( !aScale.IncrementData.Distance.hasValue() )
aExplicitIncrementDest.Distance = aExplicitIncrementSource.Distance;
bool bAutoMinorInterval = true;
if( aScale.IncrementData.SubIncrements.getLength() )
bAutoMinorInterval = !( aScale.IncrementData.SubIncrements[0].IntervalCount.hasValue() );
if( bAutoMinorInterval )
{
if( !aExplicitIncrementDest.SubIncrements.empty() && !aExplicitIncrementSource.SubIncrements.empty() )
aExplicitIncrementDest.SubIncrements[0].IntervalCount =
aExplicitIncrementSource.SubIncrements[0].IntervalCount;
}
nC->setExplicitScaleAndIncrement( 1, nAxisIndex, aExplicitScaleDest, aExplicitIncrementDest );
}
}
}
if( AxisHelper::isAxisPositioningEnabled() )
{
//correct origin for y main axis (the origin is where the other main axis crosses)
sal_Int32 nAxisIndex=0;
sal_Int32 nDimensionIndex=1;
for (auto & axisUsage : m_aAxisUsageList)
{
AxisUsage& rAxisUsage = axisUsage.second;
std::vector< VCoordinateSystem* > aVCooSysList = rAxisUsage.getCoordinateSystems(nDimensionIndex,nAxisIndex);
size_t nC;
for( nC=0; nC < aVCooSysList.size(); nC++)
{
ExplicitScaleData aExplicitScale( aVCooSysList[nC]->getExplicitScale( nDimensionIndex, nAxisIndex ) );
ExplicitIncrementData aExplicitIncrement( aVCooSysList[nC]->getExplicitIncrement( nDimensionIndex, nAxisIndex ) );
Reference< chart2::XCoordinateSystem > xCooSys( aVCooSysList[nC]->getModel() );
Reference< XAxis > xAxis( xCooSys->getAxisByDimension( nDimensionIndex, nAxisIndex ) );
Reference< beans::XPropertySet > xCrossingMainAxis( AxisHelper::getCrossingMainAxis( xAxis, xCooSys ), uno::UNO_QUERY );
css::chart::ChartAxisPosition eCrossingMainAxisPos( css::chart::ChartAxisPosition_ZERO );
if( xCrossingMainAxis.is() )
{
xCrossingMainAxis->getPropertyValue("CrossoverPosition") >>= eCrossingMainAxisPos;
if( eCrossingMainAxisPos == css::chart::ChartAxisPosition_VALUE )
{
double fValue = 0.0;
xCrossingMainAxis->getPropertyValue("CrossoverValue") >>= fValue;
aExplicitScale.Origin = fValue;
}
else if( eCrossingMainAxisPos == css::chart::ChartAxisPosition_ZERO )
aExplicitScale.Origin = 0.0;
else if( eCrossingMainAxisPos == css::chart::ChartAxisPosition_START )
aExplicitScale.Origin = aExplicitScale.Minimum;
else if( eCrossingMainAxisPos == css::chart::ChartAxisPosition_END )
aExplicitScale.Origin = aExplicitScale.Maximum;
}
aVCooSysList[nC]->setExplicitScaleAndIncrement( nDimensionIndex, nAxisIndex, aExplicitScale, aExplicitIncrement );
}
}
}
}
drawing::Direction3D SeriesPlotterContainer::getPreferredAspectRatio()
{
drawing::Direction3D aPreferredAspectRatio(1.0,1.0,1.0);
sal_Int32 nPlotterCount=0;
//get a list of all preferred aspect ratios and combine them
//first with special demands wins (less or equal zero <-> arbitrary)
double fx, fy, fz;
fx = fy = fz = -1.0;
for( std::unique_ptr<VSeriesPlotter>& aPlotter : m_aSeriesPlotterList )
{
drawing::Direction3D aSingleRatio( aPlotter->getPreferredDiagramAspectRatio() );
if( fx<0 && aSingleRatio.DirectionX>0 )
fx = aSingleRatio.DirectionX;
if( fy<0 && aSingleRatio.DirectionY>0 )
{
if( fx>0 && aSingleRatio.DirectionX>0 )
fy = fx*aSingleRatio.DirectionY/aSingleRatio.DirectionX;
else if( fz>0 && aSingleRatio.DirectionZ>0 )
fy = fz*aSingleRatio.DirectionY/aSingleRatio.DirectionZ;
else
fy = aSingleRatio.DirectionY;
}
if( fz<0 && aSingleRatio.DirectionZ>0 )
{
if( fx>0 && aSingleRatio.DirectionX>0 )
fz = fx*aSingleRatio.DirectionZ/aSingleRatio.DirectionX;
else if( fy>0 && aSingleRatio.DirectionY>0 )
fz = fy*aSingleRatio.DirectionZ/aSingleRatio.DirectionY;
else
fz = aSingleRatio.DirectionZ;
}
if( fx>0 && fy>0 && fz>0 )
break;
++nPlotterCount;
}
aPreferredAspectRatio = drawing::Direction3D(fx, fy, fz);
return aPreferredAspectRatio;
}
}
struct CreateShapeParam2D
{
css::awt::Rectangle maRemainingSpace;
std::shared_ptr<SeriesPlotterContainer> mpSeriesPlotterContainer;
std::shared_ptr<VTitle> mpVTitleX;
std::shared_ptr<VTitle> mpVTitleY;
std::shared_ptr<VTitle> mpVTitleZ;
std::shared_ptr<VTitle> mpVTitleSecondX;
std::shared_ptr<VTitle> mpVTitleSecondY;
css::uno::Reference<css::drawing::XShape> mxMarkHandles;
css::uno::Reference<css::drawing::XShape> mxPlotAreaWithAxes;
css::uno::Reference<css::drawing::XShapes> mxDiagramWithAxesShapes;
bool mbAutoPosTitleX;
bool mbAutoPosTitleY;
bool mbAutoPosTitleZ;
bool mbAutoPosSecondTitleX;
bool mbAutoPosSecondTitleY;
bool mbUseFixedInnerSize;
CreateShapeParam2D() :
mbAutoPosTitleX(true),
mbAutoPosTitleY(true),
mbAutoPosTitleZ(true),
mbAutoPosSecondTitleX(true),
mbAutoPosSecondTitleY(true),
mbUseFixedInnerSize(false) {}
};
const uno::Sequence<sal_Int8>& ExplicitValueProvider::getUnoTunnelId()
{
return theExplicitValueProviderUnoTunnelId::get().getSeq();
}
ExplicitValueProvider* ExplicitValueProvider::getExplicitValueProvider(
const Reference< uno::XInterface >& xChartView )
{
ExplicitValueProvider* pExplicitValueProvider=nullptr;
Reference< lang::XUnoTunnel > xTunnel( xChartView, uno::UNO_QUERY );
if( xTunnel.is() )
{
pExplicitValueProvider = reinterpret_cast<ExplicitValueProvider*>(xTunnel->getSomething(
ExplicitValueProvider::getUnoTunnelId() ));
}
return pExplicitValueProvider;
}
ChartView::ChartView(
uno::Reference<uno::XComponentContext> const & xContext,
ChartModel& rModel)
: m_aMutex()
, m_xCC(xContext)
, mrChartModel(rModel)
, m_xShapeFactory()
, m_xDrawPage()
, m_pDrawModelWrapper()
, m_aListenerContainer( m_aMutex )
, m_bViewDirty(true)
, m_bInViewUpdate(false)
, m_bViewUpdatePending(false)
, m_bRefreshAddIn(true)
, m_aPageResolution(1000,1000)
, m_bPointsWereSkipped(false)
, m_nScaleXNumerator(1)
, m_nScaleXDenominator(1)
, m_nScaleYNumerator(1)
, m_nScaleYDenominator(1)
, m_bSdrViewIsInEditMode(false)
, m_aResultingDiagramRectangleExcludingAxes(0,0,0,0)
{
init();
}
void ChartView::init()
{
if( !m_pDrawModelWrapper.get() )
{
SolarMutexGuard aSolarGuard;
m_pDrawModelWrapper = std::make_shared< DrawModelWrapper >();
m_xShapeFactory = m_pDrawModelWrapper->getShapeFactory();
m_xDrawPage = m_pDrawModelWrapper->getMainDrawPage();
StartListening( m_pDrawModelWrapper->getSdrModel() );
}
}
void SAL_CALL ChartView::initialize( const uno::Sequence< uno::Any >& )
{
init();
}
ChartView::~ChartView()
{
maTimeBased.maTimer.Stop();
// #i120831#. In ChartView::initialize(), m_xShapeFactory is created from SdrModel::getUnoModel() and indirectly
// from SfxBaseModel, it needs call dispose() to make sure SfxBaseModel object is freed correctly.
uno::Reference< lang::XComponent > xComp( m_xShapeFactory, uno::UNO_QUERY);
if ( xComp.is() )
xComp->dispose();
if( m_pDrawModelWrapper.get() )
{
SolarMutexGuard aSolarGuard;
EndListening( m_pDrawModelWrapper->getSdrModel() );
m_pDrawModelWrapper.reset();
}
m_xDrawPage = nullptr;
impl_deleteCoordinateSystems();
}
void ChartView::impl_deleteCoordinateSystems()
{
//delete all coordinate systems
m_aVCooSysList.clear();//#i109770#
}
// datatransfer::XTransferable
namespace
{
const OUString lcl_aGDIMetaFileMIMEType(
"application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"" );
const OUString lcl_aGDIMetaFileMIMETypeHighContrast(
"application/x-openoffice-highcontrast-gdimetafile;windows_formatname=\"GDIMetaFile\"" );
} // anonymous namespace
void ChartView::getMetaFile( const uno::Reference< io::XOutputStream >& xOutStream
, bool bUseHighContrast )
{
if( !m_xDrawPage.is() )
return;
// creating the graphic exporter
uno::Reference< drawing::XGraphicExportFilter > xExporter = drawing::GraphicExportFilter::create( m_xCC );
uno::Sequence< beans::PropertyValue > aProps(3);
aProps[0].Name = "FilterName";
aProps[0].Value <<= OUString("SVM");
aProps[1].Name = "OutputStream";
aProps[1].Value <<= xOutStream;
uno::Sequence< beans::PropertyValue > aFilterData(8);
aFilterData[0].Name = "ExportOnlyBackground";
aFilterData[0].Value <<= false;
aFilterData[1].Name = "HighContrast";
aFilterData[1].Value <<= bUseHighContrast;
aFilterData[2].Name = "Version";
const sal_Int32 nVersion = SOFFICE_FILEFORMAT_50;
aFilterData[2].Value <<= nVersion;
aFilterData[3].Name = "CurrentPage";
aFilterData[3].Value <<= uno::Reference< uno::XInterface >( m_xDrawPage, uno::UNO_QUERY );
//#i75867# poor quality of ole's alternative view with 3D scenes and zoomfactors besides 100%
aFilterData[4].Name = "ScaleXNumerator";
aFilterData[4].Value <<= m_nScaleXNumerator;
aFilterData[5].Name = "ScaleXDenominator";
aFilterData[5].Value <<= m_nScaleXDenominator;
aFilterData[6].Name = "ScaleYNumerator";
aFilterData[6].Value <<= m_nScaleYNumerator;
aFilterData[7].Name = "ScaleYDenominator";
aFilterData[7].Value <<= m_nScaleYDenominator;
aProps[2].Name = "FilterData";
aProps[2].Value <<= aFilterData;
xExporter->setSourceDocument( uno::Reference< lang::XComponent >( m_xDrawPage, uno::UNO_QUERY) );
if( xExporter->filter( aProps ) )
{
xOutStream->flush();
xOutStream->closeOutput();
uno::Reference< io::XSeekable > xSeekable( xOutStream, uno::UNO_QUERY );
if( xSeekable.is() )
xSeekable->seek(0);
}
}
uno::Any SAL_CALL ChartView::getTransferData( const datatransfer::DataFlavor& aFlavor )
{
bool bHighContrastMetaFile( aFlavor.MimeType == lcl_aGDIMetaFileMIMETypeHighContrast);
uno::Any aRet;
if( ! (bHighContrastMetaFile || aFlavor.MimeType == lcl_aGDIMetaFileMIMEType) )
return aRet;
update();
SvMemoryStream aStream( 1024, 1024 );
utl::OStreamWrapper* pStreamWrapper = new utl::OStreamWrapper( aStream );
uno::Reference< io::XOutputStream > xOutStream( pStreamWrapper );
uno::Reference< io::XInputStream > xInStream( pStreamWrapper );
uno::Reference< io::XSeekable > xSeekable( pStreamWrapper );
if( xOutStream.is() )
{
this->getMetaFile( xOutStream, bHighContrastMetaFile );
if( xInStream.is() && xSeekable.is() )
{
xSeekable->seek(0);
sal_Int32 nBytesToRead = xInStream->available();
uno::Sequence< sal_Int8 > aSeq( nBytesToRead );
xInStream->readBytes( aSeq, nBytesToRead);
aRet <<= aSeq;
xInStream->closeInput();
}
}
return aRet;
}
uno::Sequence< datatransfer::DataFlavor > SAL_CALL ChartView::getTransferDataFlavors()
{
uno::Sequence< datatransfer::DataFlavor > aRet(2);
aRet[0] = datatransfer::DataFlavor( lcl_aGDIMetaFileMIMEType,
"GDIMetaFile",
cppu::UnoType<uno::Sequence< sal_Int8 >>::get() );
aRet[1] = datatransfer::DataFlavor( lcl_aGDIMetaFileMIMETypeHighContrast,
"GDIMetaFile",
cppu::UnoType<uno::Sequence< sal_Int8 >>::get() );
return aRet;
}
sal_Bool SAL_CALL ChartView::isDataFlavorSupported( const datatransfer::DataFlavor& aFlavor )
{
return ( aFlavor.MimeType == lcl_aGDIMetaFileMIMEType ||
aFlavor.MimeType == lcl_aGDIMetaFileMIMETypeHighContrast );
}
// ____ XUnoTunnel ___
::sal_Int64 SAL_CALL ChartView::getSomething( const uno::Sequence< ::sal_Int8 >& aIdentifier )
{
if( aIdentifier.getLength() == 16 && memcmp( ExplicitValueProvider::getUnoTunnelId().getConstArray(),
aIdentifier.getConstArray(), 16 ) == 0 )
{
ExplicitValueProvider* pProvider = this;
return reinterpret_cast<sal_Int64>(pProvider);
}
return 0;
}
// lang::XServiceInfo
OUString SAL_CALL ChartView::getImplementationName()
{
return OUString(CHART_VIEW_SERVICE_IMPLEMENTATION_NAME);
}
sal_Bool SAL_CALL ChartView::supportsService( const OUString& rServiceName )
{
return cppu::supportsService(this, rServiceName);
}
css::uno::Sequence< OUString > SAL_CALL ChartView::getSupportedServiceNames()
{
return { CHART_VIEW_SERVICE_NAME };
}
::basegfx::B3DHomMatrix createTransformationSceneToScreen(
const ::basegfx::B2IRectangle& rDiagramRectangleWithoutAxes )
{
::basegfx::B3DHomMatrix aM;
aM.scale(double(rDiagramRectangleWithoutAxes.getWidth())/FIXED_SIZE_FOR_3D_CHART_VOLUME
, -double(rDiagramRectangleWithoutAxes.getHeight())/FIXED_SIZE_FOR_3D_CHART_VOLUME, 1.0 );
aM.translate(double(rDiagramRectangleWithoutAxes.getMinX())
, double(rDiagramRectangleWithoutAxes.getMinY()+rDiagramRectangleWithoutAxes.getHeight()-1), 0);
return aM;
}
namespace
{
bool lcl_IsPieOrDonut( const uno::Reference< XDiagram >& xDiagram )
{
//special treatment for pie charts
//the size is checked after complete creation to get the datalabels into the given space
//todo: this is just a workaround at the moment for pie and donut labels
return DiagramHelper::isPieOrDonutChart( xDiagram );
}
void lcl_setDefaultWritingMode( const std::shared_ptr< DrawModelWrapper >& pDrawModelWrapper, ChartModel& rModel)
{
//get writing mode from parent document:
if( SvtLanguageOptions().IsCTLFontEnabled() )
{
try
{
sal_Int16 nWritingMode=-1;
uno::Reference< beans::XPropertySet > xParentProps( rModel.getParent(), uno::UNO_QUERY );
uno::Reference< style::XStyleFamiliesSupplier > xStyleFamiliesSupplier( xParentProps, uno::UNO_QUERY );
if( xStyleFamiliesSupplier.is() )
{
uno::Reference< container::XNameAccess > xStylesFamilies( xStyleFamiliesSupplier->getStyleFamilies() );
if( xStylesFamilies.is() )
{
if( !xStylesFamilies->hasByName( "PageStyles" ) )
{
//draw/impress is parent document
uno::Reference< lang::XMultiServiceFactory > xFatcory( xParentProps, uno::UNO_QUERY );
if( xFatcory.is() )
{
uno::Reference< beans::XPropertySet > xDrawDefaults( xFatcory->createInstance( "com.sun.star.drawing.Defaults" ), uno::UNO_QUERY );
if( xDrawDefaults.is() )
xDrawDefaults->getPropertyValue( "WritingMode" ) >>= nWritingMode;
}
}
else
{
uno::Reference< container::XNameAccess > xPageStyles( xStylesFamilies->getByName( "PageStyles" ), uno::UNO_QUERY );
if( xPageStyles.is() )
{
OUString aPageStyle;
uno::Reference< text::XTextDocument > xTextDocument( xParentProps, uno::UNO_QUERY );
if( xTextDocument.is() )
{
//writer is parent document
//retrieve the current page style from the text cursor property PageStyleName
uno::Reference< text::XTextEmbeddedObjectsSupplier > xTextEmbeddedObjectsSupplier( xTextDocument, uno::UNO_QUERY );
if( xTextEmbeddedObjectsSupplier.is() )
{
uno::Reference< container::XNameAccess > xEmbeddedObjects( xTextEmbeddedObjectsSupplier->getEmbeddedObjects() );
if( xEmbeddedObjects.is() )
{
uno::Sequence< OUString > aNames( xEmbeddedObjects->getElementNames() );
sal_Int32 nCount = aNames.getLength();
for( sal_Int32 nN=0; nN<nCount; nN++ )
{
uno::Reference< beans::XPropertySet > xEmbeddedProps( xEmbeddedObjects->getByName( aNames[nN] ), uno::UNO_QUERY );
if( xEmbeddedProps.is() )
{
static OUString aChartCLSID = SvGlobalName( SO3_SCH_CLASSID ).GetHexName();
OUString aCLSID;
xEmbeddedProps->getPropertyValue( "CLSID" ) >>= aCLSID;
if( aCLSID == aChartCLSID )
{
uno::Reference< text::XTextContent > xEmbeddedObject( xEmbeddedProps, uno::UNO_QUERY );
if( xEmbeddedObject.is() )
{
uno::Reference< text::XTextRange > xAnchor( xEmbeddedObject->getAnchor() );
if( xAnchor.is() )
{
uno::Reference< beans::XPropertySet > xAnchorProps( xAnchor, uno::UNO_QUERY );
if( xAnchorProps.is() )
{
xAnchorProps->getPropertyValue( "WritingMode" ) >>= nWritingMode;
}
uno::Reference< text::XText > xText( xAnchor->getText() );
if( xText.is() )
{
uno::Reference< beans::XPropertySet > xTextCursorProps( xText->createTextCursor(), uno::UNO_QUERY );
if( xTextCursorProps.is() )
xTextCursorProps->getPropertyValue( "PageStyleName" ) >>= aPageStyle;
}
}
}
break;
}
}
}
}
}
if( aPageStyle.isEmpty() )
{
uno::Reference< text::XText > xText( xTextDocument->getText() );
if( xText.is() )
{
uno::Reference< beans::XPropertySet > xTextCursorProps( xText->createTextCursor(), uno::UNO_QUERY );
if( xTextCursorProps.is() )
xTextCursorProps->getPropertyValue( "PageStyleName" ) >>= aPageStyle;
}
}
}
else
{
//Calc is parent document
xParentProps->getPropertyValue( "PageStyle" ) >>= aPageStyle;
if(aPageStyle.isEmpty())
aPageStyle = "Default";
}
if( nWritingMode == -1 || nWritingMode == text::WritingMode2::PAGE )
{
uno::Reference< beans::XPropertySet > xPageStyle( xPageStyles->getByName( aPageStyle ), uno::UNO_QUERY );
if( xPageStyle.is() )
xPageStyle->getPropertyValue( "WritingMode" ) >>= nWritingMode;
}
}
}
}
}
if( nWritingMode != -1 && nWritingMode != text::WritingMode2::PAGE )
{
if( pDrawModelWrapper.get() )
pDrawModelWrapper->GetItemPool().SetPoolDefaultItem(SvxFrameDirectionItem(static_cast<SvxFrameDirection>(nWritingMode), EE_PARA_WRITINGDIR) );
}
}
catch( const uno::Exception& )
{
DBG_UNHANDLED_EXCEPTION("chart2" );
}
}
}
sal_Int16 lcl_getDefaultWritingModeFromPool( const std::shared_ptr<DrawModelWrapper>& pDrawModelWrapper )
{
sal_Int16 nWritingMode = text::WritingMode2::LR_TB;
if(!pDrawModelWrapper)
return nWritingMode;
const SfxPoolItem* pItem = &(pDrawModelWrapper->GetItemPool().GetDefaultItem( EE_PARA_WRITINGDIR ));
if( pItem )
nWritingMode = static_cast< sal_Int16 >(static_cast< const SvxFrameDirectionItem * >( pItem )->GetValue());
return nWritingMode;
}
} //end anonymous namespace
awt::Rectangle ChartView::impl_createDiagramAndContent( const CreateShapeParam2D& rParam, const awt::Size& rPageSize )
{
//return the used rectangle
awt::Rectangle aUsedOuterRect(rParam.maRemainingSpace.X, rParam.maRemainingSpace.Y, 0, 0);
uno::Reference< XDiagram > xDiagram( mrChartModel.getFirstDiagram() );
if( !xDiagram.is())
return aUsedOuterRect;
sal_Int32 nDimensionCount = DiagramHelper::getDimension( xDiagram );
if(!nDimensionCount)
{
//@todo handle mixed dimension
nDimensionCount = 2;
}
basegfx::B2IRectangle aAvailableOuterRect = BaseGFXHelper::makeRectangle(rParam.maRemainingSpace);
const std::vector< std::unique_ptr<VCoordinateSystem> >& rVCooSysList( rParam.mpSeriesPlotterContainer->getCooSysList() );
SeriesPlottersType& rSeriesPlotterList = rParam.mpSeriesPlotterContainer->getSeriesPlotterList();
//create VAxis, so they can give necessary information for automatic scaling
uno::Reference<chart2::XChartDocument> const xChartDoc(&mrChartModel);
uno::Reference<util::XNumberFormatsSupplier> const xNumberFormatsSupplier(
mrChartModel.getNumberFormatsSupplier());
size_t nC = 0;
for( nC=0; nC < rVCooSysList.size(); nC++)
{
VCoordinateSystem* pVCooSys = rVCooSysList[nC].get();
if(nDimensionCount==3)
{
uno::Reference<beans::XPropertySet> xSceneProperties( xDiagram, uno::UNO_QUERY );
CuboidPlanePosition eLeftWallPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardLeftWall( xSceneProperties ) );
CuboidPlanePosition eBackWallPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBackWall( xSceneProperties ) );
CuboidPlanePosition eBottomPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBottom( xSceneProperties ) );
pVCooSys->set3DWallPositions( eLeftWallPos, eBackWallPos, eBottomPos );
}
pVCooSys->createVAxisList(xChartDoc, rPageSize, rParam.maRemainingSpace, rParam.mbUseFixedInnerSize);
}
// - prepare list of all axis and how they are used
Date aNullDate = NumberFormatterWrapper( xNumberFormatsSupplier ).getNullDate();
rParam.mpSeriesPlotterContainer->initAxisUsageList(aNullDate);
rParam.mpSeriesPlotterContainer->doAutoScaling( mrChartModel );
rParam.mpSeriesPlotterContainer->setScalesFromCooSysToPlotter();
rParam.mpSeriesPlotterContainer->setNumberFormatsFromAxes();
//create shapes
//aspect ratio
drawing::Direction3D aPreferredAspectRatio =
rParam.mpSeriesPlotterContainer->getPreferredAspectRatio();
uno::Reference< drawing::XShapes > xSeriesTargetInFrontOfAxis;
uno::Reference< drawing::XShapes > xSeriesTargetBehindAxis;
VDiagram aVDiagram(xDiagram, aPreferredAspectRatio, nDimensionCount);
bool bIsPieOrDonut = lcl_IsPieOrDonut(xDiagram);
{//create diagram
aVDiagram.init(rParam.mxDiagramWithAxesShapes, m_xShapeFactory);
aVDiagram.createShapes(
awt::Point(rParam.maRemainingSpace.X, rParam.maRemainingSpace.Y),
awt::Size(rParam.maRemainingSpace.Width, rParam.maRemainingSpace.Height));
xSeriesTargetInFrontOfAxis = aVDiagram.getCoordinateRegion();
// It is preferable to use full size than minimum for pie charts
if (!bIsPieOrDonut && !rParam.mbUseFixedInnerSize)
aVDiagram.reduceToMimimumSize();
}
uno::Reference< drawing::XShapes > xTextTargetShapes =
ShapeFactory::getOrCreateShapeFactory(m_xShapeFactory)->createGroup2D(rParam.mxDiagramWithAxesShapes);
// - create axis and grids for all coordinate systems
//init all coordinate systems
for( nC=0; nC < rVCooSysList.size(); nC++)
{
VCoordinateSystem* pVCooSys = rVCooSysList[nC].get();
pVCooSys->initPlottingTargets(xSeriesTargetInFrontOfAxis,xTextTargetShapes,m_xShapeFactory,xSeriesTargetBehindAxis);
pVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
createTransformationSceneToScreen( aVDiagram.getCurrentRectangle() ) ));
pVCooSys->initVAxisInList();
}
//calculate resulting size respecting axis label layout and fontscaling
uno::Reference< drawing::XShape > xBoundingShape(rParam.mxDiagramWithAxesShapes, uno::UNO_QUERY);
::basegfx::B2IRectangle aConsumedOuterRect;
//use first coosys only so far; todo: calculate for more than one coosys if we have more in future
//todo: this is just a workaround at the moment for pie and donut labels
if( !bIsPieOrDonut && (!rVCooSysList.empty()) )
{
VCoordinateSystem* pVCooSys = rVCooSysList[0].get();
pVCooSys->createMaximumAxesLabels();
aConsumedOuterRect = ShapeFactory::getRectangleOfShape(xBoundingShape);
::basegfx::B2IRectangle aNewInnerRect( aVDiagram.getCurrentRectangle() );
if (!rParam.mbUseFixedInnerSize)
aNewInnerRect = aVDiagram.adjustInnerSize( aConsumedOuterRect );
pVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
createTransformationSceneToScreen( aNewInnerRect ) ));
//redo autoscaling to get size and text dependent automatic main increment count
rParam.mpSeriesPlotterContainer->doAutoScaling( mrChartModel );
rParam.mpSeriesPlotterContainer->updateScalesAndIncrementsOnAxes();
rParam.mpSeriesPlotterContainer->setScalesFromCooSysToPlotter();
pVCooSys->createAxesLabels();
bool bLessSpaceConsumedThanExpected = false;
{
aConsumedOuterRect = ShapeFactory::getRectangleOfShape(xBoundingShape);
if( aConsumedOuterRect.getMinX() > aAvailableOuterRect.getMinX()
|| aConsumedOuterRect.getMaxX() < aAvailableOuterRect.getMaxX()
|| aConsumedOuterRect.getMinY() > aAvailableOuterRect.getMinY()
|| aConsumedOuterRect.getMinY() < aAvailableOuterRect.getMaxY() )
bLessSpaceConsumedThanExpected = true;
}
if (bLessSpaceConsumedThanExpected && !rParam.mbUseFixedInnerSize)
{
aVDiagram.adjustInnerSize( aConsumedOuterRect );
pVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
createTransformationSceneToScreen( aVDiagram.getCurrentRectangle() ) ));
}
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
}
//create axes and grids for the final size
for( nC=0; nC < rVCooSysList.size(); nC++)
{
VCoordinateSystem* pVCooSys = rVCooSysList[nC].get();
pVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
createTransformationSceneToScreen( aVDiagram.getCurrentRectangle() ) ));
pVCooSys->createAxesShapes();
pVCooSys->createGridShapes();
}
// - create data series for all charttypes
m_bPointsWereSkipped = false;
for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
{
VSeriesPlotter* pSeriesPlotter = aPlotter.get();
uno::Reference< drawing::XShapes > xSeriesTarget;
if( pSeriesPlotter->WantToPlotInFrontOfAxisLine() )
xSeriesTarget = xSeriesTargetInFrontOfAxis;
else
{
xSeriesTarget = xSeriesTargetBehindAxis;
OSL_ENSURE( !bIsPieOrDonut, "not implemented yet! - during a complete recreation this shape is destroyed so no series can be created anymore" );
}
pSeriesPlotter->initPlotter( xSeriesTarget,xTextTargetShapes,m_xShapeFactory,OUString() );
pSeriesPlotter->setPageReferenceSize( rPageSize );
VCoordinateSystem* pVCooSys = lcl_getCooSysForPlotter( rVCooSysList, pSeriesPlotter );
if(nDimensionCount==2)
pSeriesPlotter->setTransformationSceneToScreen( pVCooSys->getTransformationSceneToScreen() );
//better performance for big data
{
//calculate resolution for coordinate system
Sequence<sal_Int32> aCoordinateSystemResolution = pVCooSys->getCoordinateSystemResolution( rPageSize, m_aPageResolution );
pSeriesPlotter->setCoordinateSystemResolution( aCoordinateSystemResolution );
}
pSeriesPlotter->createShapes();
m_bPointsWereSkipped = m_bPointsWereSkipped || pSeriesPlotter->PointsWereSkipped();
}
//recreate all with corrected sizes if requested
if( bIsPieOrDonut )
{
m_bPointsWereSkipped = false;
aConsumedOuterRect = ::basegfx::B2IRectangle( ShapeFactory::getRectangleOfShape(xBoundingShape) );
::basegfx::B2IRectangle aNewInnerRect( aVDiagram.getCurrentRectangle() );
if (!rParam.mbUseFixedInnerSize)
aNewInnerRect = aVDiagram.adjustInnerSize( aConsumedOuterRect );
for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
{
aPlotter->releaseShapes();
}
//clear and recreate
ShapeFactory::removeSubShapes( xSeriesTargetInFrontOfAxis ); //xSeriesTargetBehindAxis is a sub shape of xSeriesTargetInFrontOfAxis and will be removed here
xSeriesTargetBehindAxis.clear();
ShapeFactory::removeSubShapes( xTextTargetShapes );
//set new transformation
for( nC=0; nC < rVCooSysList.size(); nC++)
{
VCoordinateSystem* pVCooSys = rVCooSysList[nC].get();
pVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
createTransformationSceneToScreen( aNewInnerRect ) ));
}
// - create data series for all charttypes
for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
{
VCoordinateSystem* pVCooSys = lcl_getCooSysForPlotter( rVCooSysList, aPlotter.get() );
if(nDimensionCount==2)
aPlotter->setTransformationSceneToScreen( pVCooSys->getTransformationSceneToScreen() );
aPlotter->createShapes();
m_bPointsWereSkipped = m_bPointsWereSkipped || aPlotter->PointsWereSkipped();
}
for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
aPlotter->rearrangeLabelToAvoidOverlapIfRequested(rPageSize);
}
if (rParam.mbUseFixedInnerSize)
{
aUsedOuterRect = awt::Rectangle( aConsumedOuterRect.getMinX(), aConsumedOuterRect.getMinY(), aConsumedOuterRect.getWidth(), aConsumedOuterRect.getHeight() );
}
else
aUsedOuterRect = rParam.maRemainingSpace;
bool bSnapRectToUsedArea = false;
for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
{
bSnapRectToUsedArea = aPlotter->shouldSnapRectToUsedArea();
if(bSnapRectToUsedArea)
break;
}
if(bSnapRectToUsedArea)
{
if (rParam.mbUseFixedInnerSize)
m_aResultingDiagramRectangleExcludingAxes = getRectangleOfObject( "PlotAreaExcludingAxes" );
else
{
::basegfx::B2IRectangle aConsumedInnerRect = aVDiagram.getCurrentRectangle();
m_aResultingDiagramRectangleExcludingAxes = awt::Rectangle( aConsumedInnerRect.getMinX(), aConsumedInnerRect.getMinY(), aConsumedInnerRect.getWidth(), aConsumedInnerRect.getHeight() );
}
}
else
{
if (rParam.mbUseFixedInnerSize)
m_aResultingDiagramRectangleExcludingAxes = rParam.maRemainingSpace;
else
{
::basegfx::B2IRectangle aConsumedInnerRect = aVDiagram.getCurrentRectangle();
m_aResultingDiagramRectangleExcludingAxes = awt::Rectangle( aConsumedInnerRect.getMinX(), aConsumedInnerRect.getMinY(), aConsumedInnerRect.getWidth(), aConsumedInnerRect.getHeight() );
}
}
if (rParam.mxMarkHandles.is())
{
awt::Point aPos(rParam.maRemainingSpace.X, rParam.maRemainingSpace.Y);
awt::Size aSize(rParam.maRemainingSpace.Width, rParam.maRemainingSpace.Height);
bool bPosSizeExcludeAxesProperty = true;
uno::Reference< beans::XPropertySet > xDiaProps( xDiagram, uno::UNO_QUERY_THROW );
xDiaProps->getPropertyValue("PosSizeExcludeAxes") >>= bPosSizeExcludeAxesProperty;
if (rParam.mbUseFixedInnerSize || bPosSizeExcludeAxesProperty)
{
aPos = awt::Point( m_aResultingDiagramRectangleExcludingAxes.X, m_aResultingDiagramRectangleExcludingAxes.Y );
aSize = awt::Size( m_aResultingDiagramRectangleExcludingAxes.Width, m_aResultingDiagramRectangleExcludingAxes.Height );
}
rParam.mxMarkHandles->setPosition(aPos);
rParam.mxMarkHandles->setSize(aSize);
}
return aUsedOuterRect;
}
bool ChartView::getExplicitValuesForAxis(
uno::Reference< XAxis > xAxis
, ExplicitScaleData& rExplicitScale
, ExplicitIncrementData& rExplicitIncrement )
{
impl_updateView();
if(!xAxis.is())
return false;
uno::Reference< XCoordinateSystem > xCooSys( AxisHelper::getCoordinateSystemOfAxis(xAxis, mrChartModel.getFirstDiagram() ) );
const VCoordinateSystem* pVCooSys = findInCooSysList(m_aVCooSysList,xCooSys);
if(!pVCooSys)
return false;
sal_Int32 nDimensionIndex=-1;
sal_Int32 nAxisIndex=-1;
if( AxisHelper::getIndicesForAxis( xAxis, xCooSys, nDimensionIndex, nAxisIndex ) )
{
rExplicitScale = pVCooSys->getExplicitScale(nDimensionIndex,nAxisIndex);
rExplicitIncrement = pVCooSys->getExplicitIncrement(nDimensionIndex,nAxisIndex);
if( rExplicitScale.ShiftedCategoryPosition )
{
//remove 'one' from max
if( rExplicitScale.AxisType == css::chart2::AxisType::DATE )
{
Date aMaxDate(rExplicitScale.NullDate); aMaxDate.AddDays(::rtl::math::approxFloor(rExplicitScale.Maximum));
//for explicit scales with shifted categories we need one interval more
switch( rExplicitScale.TimeResolution )
{
case css::chart::TimeUnit::DAY:
--aMaxDate;
break;
case css::chart::TimeUnit::MONTH:
aMaxDate = DateHelper::GetDateSomeMonthsAway(aMaxDate,-1);
break;
case css::chart::TimeUnit::YEAR:
aMaxDate = DateHelper::GetDateSomeYearsAway(aMaxDate,-1);
break;
}
rExplicitScale.Maximum = aMaxDate - rExplicitScale.NullDate;
}
else if( rExplicitScale.AxisType == css::chart2::AxisType::CATEGORY )
rExplicitScale.Maximum -= 1.0;
else if( rExplicitScale.AxisType == css::chart2::AxisType::SERIES )
rExplicitScale.Maximum -= 1.0;
}
return true;
}
return false;
}
SdrPage* ChartView::getSdrPage()
{
SdrPage* pPage=nullptr;
Reference< lang::XUnoTunnel> xUnoTunnel(m_xDrawPage,uno::UNO_QUERY);
if(xUnoTunnel.is())
{
SvxDrawPage* pSvxDrawPage = reinterpret_cast<SvxDrawPage*>(xUnoTunnel->getSomething(
SvxDrawPage::getUnoTunnelId() ));
if(pSvxDrawPage)
{
pPage = pSvxDrawPage->GetSdrPage();
}
}
return pPage;
}
uno::Reference< drawing::XShape > ChartView::getShapeForCID( const OUString& rObjectCID )
{
SolarMutexGuard aSolarGuard;
SdrObject* pObj = DrawModelWrapper::getNamedSdrObject( rObjectCID, this->getSdrPage() );
if( pObj )
return uno::Reference< drawing::XShape >( pObj->getUnoShape(), uno::UNO_QUERY);
return nullptr;
}
awt::Rectangle ChartView::getDiagramRectangleExcludingAxes()
{
impl_updateView();
return m_aResultingDiagramRectangleExcludingAxes;
}
awt::Rectangle ChartView::getRectangleOfObject( const OUString& rObjectCID, bool bSnapRect )
{
impl_updateView();
awt::Rectangle aRet;
uno::Reference< drawing::XShape > xShape( getShapeForCID(rObjectCID) );
if(xShape.is())
{
//special handling for axis for old api:
//same special handling for diagram
ObjectType eObjectType( ObjectIdentifier::getObjectType( rObjectCID ) );
if( eObjectType == OBJECTTYPE_AXIS || eObjectType == OBJECTTYPE_DIAGRAM )
{
SolarMutexGuard aSolarGuard;
SvxShape* pRoot = SvxShape::getImplementation( xShape );
if( pRoot )
{
SdrObject* pRootSdrObject = pRoot->GetSdrObject();
if( pRootSdrObject )
{
SdrObjList* pRootList = pRootSdrObject->GetSubList();
if( pRootList )
{
OUString aShapeName = "MarkHandles";
if( eObjectType == OBJECTTYPE_DIAGRAM )
aShapeName = "PlotAreaIncludingAxes";
SdrObject* pShape = DrawModelWrapper::getNamedSdrObject( aShapeName, pRootList );
if( pShape )
xShape.set( pShape->getUnoShape(), uno::UNO_QUERY);
}
}
}
}
awt::Size aSize( xShape->getSize() );
awt::Point aPoint( xShape->getPosition() );
aRet = awt::Rectangle( aPoint.X, aPoint.Y, aSize.Width, aSize.Height );
if( bSnapRect )
{
//for rotated objects the shape size and position differs from the visible rectangle
SvxShape* pShape = SvxShape::getImplementation( xShape );
if( pShape )
{
SdrObject* pSdrObject = pShape->GetSdrObject();
if( pSdrObject )
{
tools::Rectangle aSnapRect( pSdrObject->GetSnapRect() );
aRet = awt::Rectangle(aSnapRect.Left(),aSnapRect.Top(),aSnapRect.GetWidth(),aSnapRect.GetHeight());
}
}
}
}
return aRet;
}
std::shared_ptr< DrawModelWrapper > ChartView::getDrawModelWrapper()
{
return m_pDrawModelWrapper;
}
namespace
{
inline sal_Int32 lcl_getDiagramTitleSpace()
{
return 200; //=0,2 cm spacing
}
bool lcl_getPropertySwapXAndYAxis( const uno::Reference< XDiagram >& xDiagram )
{
bool bSwapXAndY = false;
uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );
if( xCooSysContainer.is() )
{
uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
if( aCooSysList.getLength() )
{
uno::Reference<beans::XPropertySet> xProp(aCooSysList[0], uno::UNO_QUERY );
if( xProp.is()) try
{
xProp->getPropertyValue( "SwapXAndYAxis" ) >>= bSwapXAndY;
}
catch( const uno::Exception& e )
{
SAL_WARN("chart2", "Exception caught. " << e );
}
}
}
return bSwapXAndY;
}
}
sal_Int32 ExplicitValueProvider::getExplicitNumberFormatKeyForAxis(
const Reference< chart2::XAxis >& xAxis
, const Reference< chart2::XCoordinateSystem > & xCorrespondingCoordinateSystem
, const Reference<chart2::XChartDocument>& xChartDoc)
{
return AxisHelper::getExplicitNumberFormatKeyForAxis( xAxis, xCorrespondingCoordinateSystem, xChartDoc
, true /*bSearchForParallelAxisIfNothingIsFound*/ );
}
sal_Int32 ExplicitValueProvider::getExplicitNumberFormatKeyForDataLabel(
const uno::Reference< beans::XPropertySet >& xSeriesOrPointProp,
const uno::Reference< XDataSeries >& xSeries,
sal_Int32 nPointIndex /*-1 for whole series*/,
const uno::Reference< XDiagram >& xDiagram
)
{
sal_Int32 nFormat=0;
if( !xSeriesOrPointProp.is() )
return nFormat;
bool bLinkToSource = true;
try
{
xSeriesOrPointProp->getPropertyValue(CHART_UNONAME_LINK_TO_SRC_NUMFMT) >>= bLinkToSource;
}
catch ( const beans::UnknownPropertyException& ) {}
xSeriesOrPointProp->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nFormat;
sal_Int32 nOldFormat = nFormat;
if (bLinkToSource)
{
uno::Reference< chart2::XChartType > xChartType( DataSeriesHelper::getChartTypeOfSeries( xSeries, xDiagram ) );
bool bFormatFound = false;
if( ChartTypeHelper::shouldLabelNumberFormatKeyBeDetectedFromYAxis( xChartType ) )
{
uno::Reference< beans::XPropertySet > xAttachedAxisProps( DiagramHelper::getAttachedAxis( xSeries, xDiagram ), uno::UNO_QUERY );
if (xAttachedAxisProps.is() && (xAttachedAxisProps->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nFormat))
bFormatFound = true;
}
if( !bFormatFound )
{
Reference< chart2::data::XDataSource > xSeriesSource( xSeries, uno::UNO_QUERY );
OUString aRole( ChartTypeHelper::getRoleOfSequenceForDataLabelNumberFormatDetection( xChartType ) );
Reference< data::XLabeledDataSequence > xLabeledSequence(
DataSeriesHelper::getDataSequenceByRole( xSeriesSource, aRole ));
if( xLabeledSequence.is() )
{
Reference< data::XDataSequence > xValues( xLabeledSequence->getValues() );
if( xValues.is() )
nFormat = xValues->getNumberFormatKeyByIndex( nPointIndex );
}
}
if (nFormat >= 0 && nOldFormat != nFormat)
xSeriesOrPointProp->setPropertyValue(CHART_UNONAME_NUMFMT, uno::Any(nFormat));
}
if(nFormat<0)
nFormat=0;
return nFormat;
}
sal_Int32 ExplicitValueProvider::getExplicitPercentageNumberFormatKeyForDataLabel(
const uno::Reference< beans::XPropertySet >& xSeriesOrPointProp,
const uno::Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier )
{
sal_Int32 nFormat=0;
if( !xSeriesOrPointProp.is() )
return nFormat;
if( !(xSeriesOrPointProp->getPropertyValue("PercentageNumberFormat") >>= nFormat) )
{
nFormat = DiagramHelper::getPercentNumberFormat( xNumberFormatsSupplier );
}
if(nFormat<0)
nFormat=0;
return nFormat;
}
awt::Rectangle ExplicitValueProvider::AddSubtractAxisTitleSizes(
ChartModel& rModel
, const Reference< uno::XInterface >& xChartView
, const awt::Rectangle& rPositionAndSize, bool bSubtract )
{
awt::Rectangle aRet(rPositionAndSize);
//add axis title sizes to the diagram size
uno::Reference< chart2::XTitle > xTitle_Height( TitleHelper::getTitle( TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION, rModel ) );
uno::Reference< chart2::XTitle > xTitle_Width( TitleHelper::getTitle( TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION, rModel ) );
uno::Reference< chart2::XTitle > xSecondTitle_Height( TitleHelper::getTitle( TitleHelper::SECONDARY_X_AXIS_TITLE, rModel ) );
uno::Reference< chart2::XTitle > xSecondTitle_Width( TitleHelper::getTitle( TitleHelper::SECONDARY_Y_AXIS_TITLE, rModel ) );
if( xTitle_Height.is() || xTitle_Width.is() || xSecondTitle_Height.is() || xSecondTitle_Width.is() )
{
ExplicitValueProvider* pExplicitValueProvider = ExplicitValueProvider::getExplicitValueProvider(xChartView);
if( pExplicitValueProvider )
{
//detect whether x axis points into x direction or not
if( lcl_getPropertySwapXAndYAxis( rModel.getFirstDiagram() ) )
{
std::swap( xTitle_Height, xTitle_Width );
std::swap( xSecondTitle_Height, xSecondTitle_Width );
}
sal_Int32 nTitleSpaceWidth = 0;
sal_Int32 nTitleSpaceHeight = 0;
sal_Int32 nSecondTitleSpaceWidth = 0;
sal_Int32 nSecondTitleSpaceHeight = 0;
if( xTitle_Height.is() )
{
OUString aCID_X( ObjectIdentifier::createClassifiedIdentifierForObject( xTitle_Height, rModel ) );
nTitleSpaceHeight = pExplicitValueProvider->getRectangleOfObject( aCID_X, true ).Height;
if( nTitleSpaceHeight )
nTitleSpaceHeight+=lcl_getDiagramTitleSpace();
}
if( xTitle_Width.is() )
{
OUString aCID_Y( ObjectIdentifier::createClassifiedIdentifierForObject( xTitle_Width, rModel ) );
nTitleSpaceWidth = pExplicitValueProvider->getRectangleOfObject( aCID_Y, true ).Width;
if(nTitleSpaceWidth)
nTitleSpaceWidth+=lcl_getDiagramTitleSpace();
}
if( xSecondTitle_Height.is() )
{
OUString aCID_X( ObjectIdentifier::createClassifiedIdentifierForObject( xSecondTitle_Height, rModel ) );
nSecondTitleSpaceHeight = pExplicitValueProvider->getRectangleOfObject( aCID_X, true ).Height;
if( nSecondTitleSpaceHeight )
nSecondTitleSpaceHeight+=lcl_getDiagramTitleSpace();
}
if( xSecondTitle_Width.is() )
{
OUString aCID_Y( ObjectIdentifier::createClassifiedIdentifierForObject( xSecondTitle_Width, rModel ) );
nSecondTitleSpaceWidth += pExplicitValueProvider->getRectangleOfObject( aCID_Y, true ).Width;
if( nSecondTitleSpaceWidth )
nSecondTitleSpaceWidth+=lcl_getDiagramTitleSpace();
}
if( bSubtract )
{
aRet.X += nTitleSpaceWidth;
aRet.Y += nSecondTitleSpaceHeight;
aRet.Width -= (nTitleSpaceWidth + nSecondTitleSpaceWidth);
aRet.Height -= (nTitleSpaceHeight + nSecondTitleSpaceHeight);
}
else
{
aRet.X -= nTitleSpaceWidth;
aRet.Y -= nSecondTitleSpaceHeight;
aRet.Width += nTitleSpaceWidth + nSecondTitleSpaceWidth;
aRet.Height += nTitleSpaceHeight + nSecondTitleSpaceHeight;
}
}
}
return aRet;
}
namespace {
inline double lcl_getPageLayoutDistancePercentage()
{
return 0.02;
}
bool getAvailablePosAndSizeForDiagram(
CreateShapeParam2D& rParam, const awt::Size & rPageSize, const uno::Reference<XDiagram>& xDiagram )
{
rParam.mbUseFixedInnerSize = false;
//@todo: we need a size dependent on the axis labels
sal_Int32 nYDistance = static_cast<sal_Int32>(rPageSize.Height*lcl_getPageLayoutDistancePercentage());
sal_Int32 nXDistance = static_cast<sal_Int32>(rPageSize.Width*lcl_getPageLayoutDistancePercentage());
rParam.maRemainingSpace.X += nXDistance;
rParam.maRemainingSpace.Width -= 2*nXDistance;
rParam.maRemainingSpace.Y += nYDistance;
rParam.maRemainingSpace.Height -= 2*nYDistance;
if (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0)
return false;
uno::Reference< beans::XPropertySet > xProp(xDiagram, uno::UNO_QUERY);
bool bPosSizeExcludeAxes = false;
if( xProp.is() )
xProp->getPropertyValue( "PosSizeExcludeAxes" ) >>= bPosSizeExcludeAxes;
//size:
css::chart2::RelativeSize aRelativeSize;
if( xProp.is() && (xProp->getPropertyValue( "RelativeSize" )>>=aRelativeSize) )
{
rParam.maRemainingSpace.Height = static_cast<sal_Int32>(aRelativeSize.Secondary*rPageSize.Height);
rParam.maRemainingSpace.Width = static_cast<sal_Int32>(aRelativeSize.Primary*rPageSize.Width);
rParam.mbUseFixedInnerSize = bPosSizeExcludeAxes;
}
//position:
chart2::RelativePosition aRelativePosition;
if( xProp.is() && (xProp->getPropertyValue( "RelativePosition" )>>=aRelativePosition) )
{
//@todo decide whether x is primary or secondary
//the coordinates re relative to the page
double fX = aRelativePosition.Primary*rPageSize.Width;
double fY = aRelativePosition.Secondary*rPageSize.Height;
awt::Point aPos = RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
awt::Point(static_cast<sal_Int32>(fX),static_cast<sal_Int32>(fY)),
awt::Size(rParam.maRemainingSpace.Width, rParam.maRemainingSpace.Height),
aRelativePosition.Anchor);
rParam.maRemainingSpace.X = aPos.X;
rParam.maRemainingSpace.Y = aPos.Y;
rParam.mbUseFixedInnerSize = bPosSizeExcludeAxes;
}
//ensure that the diagram does not lap out right side or out of bottom
if (rParam.maRemainingSpace.Y + rParam.maRemainingSpace.Height > rPageSize.Height)
rParam.maRemainingSpace.Height = rPageSize.Height - rParam.maRemainingSpace.Y;
if (rParam.maRemainingSpace.X + rParam.maRemainingSpace.Width > rPageSize.Width)
rParam.maRemainingSpace.Width = rPageSize.Width - rParam.maRemainingSpace.X;
return true;
}
enum TitleAlignment { ALIGN_LEFT, ALIGN_TOP, ALIGN_RIGHT, ALIGN_BOTTOM, ALIGN_Z };
void changePositionOfAxisTitle( VTitle* pVTitle, TitleAlignment eAlignment
, awt::Rectangle const & rDiagramPlusAxesRect, const awt::Size & rPageSize )
{
if(!pVTitle)
return;
awt::Point aNewPosition(0,0);
awt::Size aTitleSize = pVTitle->getFinalSize();
sal_Int32 nYDistance = static_cast<sal_Int32>(rPageSize.Height*lcl_getPageLayoutDistancePercentage());
sal_Int32 nXDistance = static_cast<sal_Int32>(rPageSize.Width*lcl_getPageLayoutDistancePercentage());
switch( eAlignment )
{
case ALIGN_TOP:
aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width/2
, rDiagramPlusAxesRect.Y - aTitleSize.Height/2 - nYDistance );
break;
case ALIGN_BOTTOM:
aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width/2
, rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height + aTitleSize.Height/2 + nYDistance );
break;
case ALIGN_LEFT:
aNewPosition = awt::Point( rDiagramPlusAxesRect.X - aTitleSize.Width/2 - nXDistance
, rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height/2 );
break;
case ALIGN_RIGHT:
aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width + aTitleSize.Width/2 + nXDistance
, rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height/2 );
break;
case ALIGN_Z:
aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width + aTitleSize.Width/2 + nXDistance
, rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height - aTitleSize.Height/2 );
break;
default:
break;
}
sal_Int32 nMaxY = rPageSize.Height - aTitleSize.Height/2;
sal_Int32 nMaxX = rPageSize.Width - aTitleSize.Width/2;
sal_Int32 nMinX = aTitleSize.Width/2;
sal_Int32 nMinY = aTitleSize.Height/2;
if( aNewPosition.Y > nMaxY )
aNewPosition.Y = nMaxY;
if( aNewPosition.X > nMaxX )
aNewPosition.X = nMaxX;
if( aNewPosition.Y < nMinY )
aNewPosition.Y = nMinY;
if( aNewPosition.X < nMinX )
aNewPosition.X = nMinX;
pVTitle->changePosition( aNewPosition );
}
std::shared_ptr<VTitle> lcl_createTitle( TitleHelper::eTitleType eType
, const uno::Reference< drawing::XShapes>& xPageShapes
, const uno::Reference< lang::XMultiServiceFactory>& xShapeFactory
, ChartModel& rModel
, awt::Rectangle& rRemainingSpace
, const awt::Size & rPageSize
, TitleAlignment eAlignment
, bool& rbAutoPosition )
{
std::shared_ptr<VTitle> apVTitle;
// #i109336# Improve auto positioning in chart
double fPercentage = lcl_getPageLayoutDistancePercentage();
sal_Int32 nXDistance = static_cast< sal_Int32 >( rPageSize.Width * fPercentage );
sal_Int32 nYDistance = static_cast< sal_Int32 >( rPageSize.Height * fPercentage );
if ( eType == TitleHelper::MAIN_TITLE )
{
nYDistance += 135; // 1/100 mm
}
else if ( eType == TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION )
{
nYDistance = 420; // 1/100 mm
}
else if ( eType == TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION )
{
nXDistance = 450; // 1/100 mm
}
uno::Reference< XTitle > xTitle( TitleHelper::getTitle( eType, rModel ) );
OUString aCompleteString = TitleHelper::getCompleteString(xTitle);
if (aCompleteString.isEmpty())
return apVTitle;
//create title
apVTitle.reset(new VTitle(xTitle));
OUString aCID = ObjectIdentifier::createClassifiedIdentifierForObject(xTitle, rModel);
apVTitle->init(xPageShapes, xShapeFactory, aCID);
apVTitle->createShapes(awt::Point(0,0), rPageSize);
awt::Size aTitleUnrotatedSize = apVTitle->getUnrotatedSize();
awt::Size aTitleSize = apVTitle->getFinalSize();
//position
rbAutoPosition = true;
awt::Point aNewPosition(0,0);
chart2::RelativePosition aRelativePosition;
uno::Reference<beans::XPropertySet> xProp(xTitle, uno::UNO_QUERY);
if (xProp.is() && (xProp->getPropertyValue("RelativePosition") >>= aRelativePosition))
{
rbAutoPosition = false;
//@todo decide whether x is primary or secondary
double fX = aRelativePosition.Primary*rPageSize.Width;
double fY = aRelativePosition.Secondary*rPageSize.Height;
double fAnglePi = apVTitle->getRotationAnglePi();
aNewPosition = RelativePositionHelper::getCenterOfAnchoredObject(
awt::Point(static_cast<sal_Int32>(fX),static_cast<sal_Int32>(fY))
, aTitleUnrotatedSize, aRelativePosition.Anchor, fAnglePi );
}
else //auto position
{
switch( eAlignment )
{
case ALIGN_TOP:
aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width/2
, rRemainingSpace.Y + aTitleSize.Height/2 + nYDistance );
break;
case ALIGN_BOTTOM:
aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width/2
, rRemainingSpace.Y + rRemainingSpace.Height - aTitleSize.Height/2 - nYDistance );
break;
case ALIGN_LEFT:
aNewPosition = awt::Point( rRemainingSpace.X + aTitleSize.Width/2 + nXDistance
, rRemainingSpace.Y + rRemainingSpace.Height/2 );
break;
case ALIGN_RIGHT:
aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width - aTitleSize.Width/2 - nXDistance
, rRemainingSpace.Y + rRemainingSpace.Height/2 );
break;
default:
break;
}
}
apVTitle->changePosition( aNewPosition );
//remaining space
switch( eAlignment )
{
case ALIGN_TOP:
// Push the remaining space down from top.
rRemainingSpace.Y += ( aTitleSize.Height + nYDistance );
rRemainingSpace.Height -= ( aTitleSize.Height + nYDistance );
break;
case ALIGN_BOTTOM:
// Push the remaining space up from bottom.
rRemainingSpace.Height -= ( aTitleSize.Height + nYDistance );
break;
case ALIGN_LEFT:
// Push the remaining space to the right from left edge.
rRemainingSpace.X += ( aTitleSize.Width + nXDistance );
rRemainingSpace.Width -= ( aTitleSize.Width + nXDistance );
break;
case ALIGN_RIGHT:
// Push the remaining space to the left from right edge.
rRemainingSpace.Width -= ( aTitleSize.Width + nXDistance );
break;
default:
break;
}
return apVTitle;
}
bool lcl_createLegend( const uno::Reference< XLegend > & xLegend
, const uno::Reference< drawing::XShapes>& xPageShapes
, const uno::Reference< lang::XMultiServiceFactory>& xShapeFactory
, const uno::Reference< uno::XComponentContext > & xContext
, awt::Rectangle & rRemainingSpace
, const awt::Size & rPageSize
, ChartModel& rModel
, const std::vector< LegendEntryProvider* >& rLegendEntryProviderList
, sal_Int16 nDefaultWritingMode )
{
if (!VLegend::isVisible(xLegend))
return false;
VLegend aVLegend( xLegend, xContext, rLegendEntryProviderList,
xPageShapes, xShapeFactory, rModel);
aVLegend.setDefaultWritingMode( nDefaultWritingMode );
aVLegend.createShapes( awt::Size( rRemainingSpace.Width, rRemainingSpace.Height ),
rPageSize );
aVLegend.changePosition( rRemainingSpace, rPageSize );
return true;
}
void lcl_createButtons(const uno::Reference<drawing::XShapes>& xPageShapes,
const uno::Reference<lang::XMultiServiceFactory>& xShapeFactory,
ChartModel& rModel,
awt::Rectangle& rRemainingSpace)
{
uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(rModel.getDataProvider(), uno::UNO_QUERY);
if (!xPivotTableDataProvider.is())
return;
uno::Reference<beans::XPropertySet> xModelPage(rModel.getPageBackground());
awt::Size aSize(4000, 700); // size of the button
long x = 0;
if (xPivotTableDataProvider->getPageFields().hasElements())
{
x = 0;
for (css::chart2::data::PivotTableFieldEntry const & rPageFieldEntry : xPivotTableDataProvider->getPageFields())
{
std::unique_ptr<VButton> pButton(new VButton);
pButton->init(xPageShapes, xShapeFactory);
awt::Point aNewPosition = awt::Point(rRemainingSpace.X + x + 100, rRemainingSpace.Y + 100);
sal_Int32 nDimensionIndex = rPageFieldEntry.DimensionIndex;
OUString aFieldOutputDescription = xPivotTableDataProvider->getFieldOutputDescription(nDimensionIndex);
pButton->setLabel(rPageFieldEntry.Name + " | " + aFieldOutputDescription);
pButton->setCID("FieldButton.Page." + OUString::number(nDimensionIndex));
pButton->setPosition(aNewPosition);
pButton->setSize(aSize);
if (rPageFieldEntry.HasHiddenMembers)
pButton->setArrowColor(Color(0x0000FF));
pButton->createShapes(xModelPage);
x += aSize.Width + 100;
}
rRemainingSpace.Y += (aSize.Height + 100 + 100);
rRemainingSpace.Height -= (aSize.Height + 100 + 100);
}
aSize = awt::Size(3000, 700); // size of the button
if (xPivotTableDataProvider->getRowFields().hasElements())
{
x = 200;
for (css::chart2::data::PivotTableFieldEntry const & rRowFieldEntry : xPivotTableDataProvider->getRowFields())
{
std::unique_ptr<VButton> pButton(new VButton);
pButton->init(xPageShapes, xShapeFactory);
awt::Point aNewPosition = awt::Point(rRemainingSpace.X + x + 100,
rRemainingSpace.Y + rRemainingSpace.Height - aSize.Height - 100);
pButton->setLabel(rRowFieldEntry.Name);
pButton->setCID("FieldButton.Row." + OUString::number(rRowFieldEntry.DimensionIndex));
pButton->setPosition(aNewPosition);
pButton->setSize(aSize);
if ( rRowFieldEntry.Name == "Data" )
{
pButton->setBGColor( Color(0x00F6F6F6) );
pButton->showArrow( false );
}
else if (rRowFieldEntry.HasHiddenMembers)
pButton->setArrowColor(Color(0x0000FF));
pButton->createShapes(xModelPage);
x += aSize.Width + 100;
}
rRemainingSpace.Height -= (aSize.Height + 100 + 100);
}
}
void formatPage(
ChartModel& rChartModel
, const awt::Size& rPageSize
, const uno::Reference< drawing::XShapes >& xTarget
, const uno::Reference< lang::XMultiServiceFactory>& xShapeFactory
)
{
try
{
uno::Reference< beans::XPropertySet > xModelPage( rChartModel.getPageBackground());
if( ! xModelPage.is())
return;
if( !xShapeFactory.is() )
return;
//format page
tPropertyNameValueMap aNameValueMap;
PropertyMapper::getValueMap( aNameValueMap, PropertyMapper::getPropertyNameMapForFillAndLineProperties(), xModelPage );
OUString aCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, OUString() ) );
aNameValueMap.emplace( "Name", uno::Any( aCID ) ); //CID OUString
tNameSequence aNames;
tAnySequence aValues;
PropertyMapper::getMultiPropertyListsFromValueMap( aNames, aValues, aNameValueMap );
ShapeFactory* pShapeFactory = ShapeFactory::getOrCreateShapeFactory(xShapeFactory);
pShapeFactory->createRectangle(
xTarget, rPageSize, awt::Point(0, 0), aNames, aValues);
}
catch( const uno::Exception & )
{
DBG_UNHANDLED_EXCEPTION("chart2" );
}
}
void lcl_removeEmptyGroupShapes( const Reference< drawing::XShapes>& xParent )
{
if(!xParent.is())
return;
Reference< drawing::XShapeGroup > xParentGroup( xParent, uno::UNO_QUERY );
if( !xParentGroup.is() )
{
Reference< drawing::XDrawPage > xPage( xParent, uno::UNO_QUERY );
if( !xPage.is() )
return;
}
//iterate from back!
for( sal_Int32 nN = xParent->getCount(); nN--; )
{
uno::Any aAny = xParent->getByIndex( nN );
Reference< drawing::XShapes> xShapes;
if( aAny >>= xShapes )
lcl_removeEmptyGroupShapes( xShapes );
if( xShapes.is() && xShapes->getCount()==0 )
{
//remove empty group shape
Reference< drawing::XShapeGroup > xGroup( xShapes, uno::UNO_QUERY );
Reference< drawing::XShape > xShape( xShapes, uno::UNO_QUERY );
if( xGroup.is() )
xParent->remove( xShape );
}
}
}
}
void ChartView::impl_refreshAddIn()
{
if( !m_bRefreshAddIn )
return;
uno::Reference< beans::XPropertySet > xProp( static_cast< ::cppu::OWeakObject* >( &mrChartModel ), uno::UNO_QUERY );
if( xProp.is()) try
{
uno::Reference< util::XRefreshable > xAddIn;
xProp->getPropertyValue( "AddIn" ) >>= xAddIn;
if( xAddIn.is() )
{
bool bRefreshAddInAllowed = true;
xProp->getPropertyValue( "RefreshAddInAllowed" ) >>= bRefreshAddInAllowed;
if( bRefreshAddInAllowed )
xAddIn->refresh();
}
}
catch( const uno::Exception& e )
{
SAL_WARN("chart2", "Exception caught. " << e );
}
}
static const char* envChartDummyFactory = getenv("CHART_DUMMY_FACTORY");
void ChartView::createShapes()
{
osl::ResettableMutexGuard aTimedGuard(maTimeMutex);
if(mrChartModel.isTimeBased())
{
maTimeBased.bTimeBased = true;
}
//make sure add-in is refreshed after creating the shapes
const ::comphelper::ScopeGuard aGuard( [this]() { this->impl_refreshAddIn(); } );
m_aResultingDiagramRectangleExcludingAxes = awt::Rectangle(0,0,0,0);
impl_deleteCoordinateSystems();
if( m_pDrawModelWrapper )
{
SolarMutexGuard aSolarGuard;
// #i12587# support for shapes in chart
m_pDrawModelWrapper->getSdrModel().EnableUndo( false );
m_pDrawModelWrapper->clearMainDrawPage();
}
lcl_setDefaultWritingMode( m_pDrawModelWrapper, mrChartModel );
awt::Size aPageSize = mrChartModel.getVisualAreaSize( embed::Aspects::MSOLE_CONTENT );
ShapeFactory* pShapeFactory = ShapeFactory::getOrCreateShapeFactory(m_xShapeFactory);
if(!mxRootShape.is())
mxRootShape = pShapeFactory->getOrCreateChartRootShape( m_xDrawPage );
SdrPage* pPage = ChartView::getSdrPage();
if(pPage) //it is necessary to use the implementation here as the uno page does not provide a propertyset
pPage->SetSize(Size(aPageSize.Width,aPageSize.Height));
else
{
OSL_FAIL("could not set page size correctly");
}
pShapeFactory->setPageSize(mxRootShape, aPageSize);
createShapes2D(aPageSize);
// #i12587# support for shapes in chart
if ( m_pDrawModelWrapper )
{
SolarMutexGuard aSolarGuard;
m_pDrawModelWrapper->getSdrModel().EnableUndo( true );
}
if(maTimeBased.bTimeBased)
{
maTimeBased.nFrame++;
}
}
// util::XEventListener (base of XCloseListener)
void SAL_CALL ChartView::disposing( const lang::EventObject& /* rSource */ )
{
}
void ChartView::impl_updateView( bool bCheckLockedCtrler )
{
if( !m_pDrawModelWrapper )
return;
// #i12587# support for shapes in chart
if ( m_bSdrViewIsInEditMode )
{
return;
}
if (bCheckLockedCtrler && mrChartModel.hasControllersLocked())
return;
if( m_bViewDirty && !m_bInViewUpdate )
{
m_bInViewUpdate = true;
//bool bOldRefreshAddIn = m_bRefreshAddIn;
//m_bRefreshAddIn = false;
try
{
impl_notifyModeChangeListener("invalid");
//prepare draw model
{
SolarMutexGuard aSolarGuard;
m_pDrawModelWrapper->lockControllers();
}
//create chart view
{
m_bViewDirty = false;
m_bViewUpdatePending = false;
createShapes();
if( m_bViewDirty )
{
//avoid recursions due to add-in
m_bRefreshAddIn = false;
m_bViewDirty = false;
m_bViewUpdatePending = false;
//delete old chart view
createShapes();
m_bRefreshAddIn = true;
}
}
m_bViewDirty = m_bViewUpdatePending;
m_bViewUpdatePending = false;
m_bInViewUpdate = false;
}
catch( const uno::Exception& )
{
DBG_UNHANDLED_EXCEPTION("chart2" );
m_bViewDirty = m_bViewUpdatePending;
m_bViewUpdatePending = false;
m_bInViewUpdate = false;
}
{
SolarMutexGuard aSolarGuard;
m_pDrawModelWrapper->unlockControllers();
}
impl_notifyModeChangeListener("valid");
//m_bRefreshAddIn = bOldRefreshAddIn;
}
}
// ____ XModifyListener ____
void SAL_CALL ChartView::modified( const lang::EventObject& /* aEvent */ )
{
m_bViewDirty = true;
if( m_bInViewUpdate )
m_bViewUpdatePending = true;
impl_notifyModeChangeListener("dirty");
}
//SfxListener
void ChartView::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
{
//#i77362 change notification for changes on additional shapes are missing
if( m_bInViewUpdate )
return;
// #i12587# support for shapes in chart
if ( m_bSdrViewIsInEditMode )
{
uno::Reference< view::XSelectionSupplier > xSelectionSupplier( mrChartModel.getCurrentController(), uno::UNO_QUERY );
if ( xSelectionSupplier.is() )
{
OUString aSelObjCID;
uno::Any aSelObj( xSelectionSupplier->getSelection() );
aSelObj >>= aSelObjCID;
if ( !aSelObjCID.isEmpty() )
{
return;
}
}
}
const SdrHint* pSdrHint = dynamic_cast< const SdrHint* >(&rHint);
if( !pSdrHint )
return;
bool bShapeChanged = false;
switch( pSdrHint->GetKind() )
{
case SdrHintKind::ObjectChange:
bShapeChanged = true;
break;
case SdrHintKind::ObjectInserted:
bShapeChanged = true;
break;
case SdrHintKind::ObjectRemoved:
bShapeChanged = true;
break;
case SdrHintKind::ModelCleared:
bShapeChanged = true;
break;
case SdrHintKind::EndEdit:
bShapeChanged = true;
break;
default:
break;
}
if(bShapeChanged)
{
//#i76053# do not send view modified notifications for changes on the hidden page which contains e.g. the symbols for the dialogs
if( ChartView::getSdrPage() != pSdrHint->GetPage() )
bShapeChanged=false;
}
if(!bShapeChanged)
return;
mrChartModel.setModified(true);
}
void ChartView::impl_notifyModeChangeListener( const OUString& rNewMode )
{
try
{
::cppu::OInterfaceContainerHelper* pIC = m_aListenerContainer
.getContainer( cppu::UnoType<util::XModeChangeListener>::get());
if( pIC )
{
util::ModeChangeEvent aEvent( static_cast< uno::XWeak* >( this ), rNewMode );
::cppu::OInterfaceIteratorHelper aIt( *pIC );
while( aIt.hasMoreElements() )
{
uno::Reference< util::XModeChangeListener > xListener( aIt.next(), uno::UNO_QUERY );
if( xListener.is() )
xListener->modeChanged( aEvent );
}
}
}
catch( const uno::Exception& )
{
DBG_UNHANDLED_EXCEPTION("chart2");
}
}
// ____ XModeChangeBroadcaster ____
void SAL_CALL ChartView::addModeChangeListener( const uno::Reference< util::XModeChangeListener >& xListener )
{
m_aListenerContainer.addInterface(
cppu::UnoType<util::XModeChangeListener>::get(), xListener );
}
void SAL_CALL ChartView::removeModeChangeListener( const uno::Reference< util::XModeChangeListener >& xListener )
{
m_aListenerContainer.removeInterface(
cppu::UnoType<util::XModeChangeListener>::get(), xListener );
}
void SAL_CALL ChartView::addModeChangeApproveListener( const uno::Reference< util::XModeChangeApproveListener >& /* _rxListener */ )
{
}
void SAL_CALL ChartView::removeModeChangeApproveListener( const uno::Reference< util::XModeChangeApproveListener >& /* _rxListener */ )
{
}
// ____ XUpdatable ____
void SAL_CALL ChartView::update()
{
impl_updateView();
//#i100778# migrate all imported or old documents to a plot area sizing exclusive axes (in case the save settings allow for this):
//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.
//When a view update is requested (what happens for creating the metafile or displaying
//the chart in edit mode or printing) it is most likely that all necessary information are available - like the underlying spreadsheet data for example.
//Those data are important for the correct axis label sizes which are needed during conversion.
if( DiagramHelper::switchDiagramPositioningToExcludingPositioning( mrChartModel, true, false ) )
impl_updateView();
}
void SAL_CALL ChartView::updateSoft()
{
update();
}
void SAL_CALL ChartView::updateHard()
{
impl_updateView(false);
}
// ____ XPropertySet ____
Reference< beans::XPropertySetInfo > SAL_CALL ChartView::getPropertySetInfo()
{
OSL_FAIL("not implemented");
return nullptr;
}
void SAL_CALL ChartView::setPropertyValue( const OUString& rPropertyName
, const Any& rValue )
{
if( rPropertyName == "Resolution" )
{
awt::Size aNewResolution;
if( ! (rValue >>= aNewResolution) )
throw lang::IllegalArgumentException( "Property 'Resolution' requires value of type awt::Size", nullptr, 0 );
if( m_aPageResolution.Width!=aNewResolution.Width || m_aPageResolution.Height!=aNewResolution.Height )
{
//set modified only when the new resolution is higher and points were skipped before
bool bSetModified = m_bPointsWereSkipped && (m_aPageResolution.Width<aNewResolution.Width || m_aPageResolution.Height<aNewResolution.Height);
m_aPageResolution = aNewResolution;
if( bSetModified )
this->modified( lang::EventObject( static_cast< uno::XWeak* >( this ) ) );
}
}
else if( rPropertyName == "ZoomFactors" )
{
//#i75867# poor quality of ole's alternative view with 3D scenes and zoomfactors besides 100%
uno::Sequence< beans::PropertyValue > aZoomFactors;
if( ! (rValue >>= aZoomFactors) )
throw lang::IllegalArgumentException( "Property 'ZoomFactors' requires value of type Sequence< PropertyValue >", nullptr, 0 );
sal_Int32 nFilterArgs = aZoomFactors.getLength();
beans::PropertyValue* pDataValues = aZoomFactors.getArray();
while( nFilterArgs-- )
{
if ( pDataValues->Name == "ScaleXNumerator" )
pDataValues->Value >>= m_nScaleXNumerator;
else if ( pDataValues->Name == "ScaleXDenominator" )
pDataValues->Value >>= m_nScaleXDenominator;
else if ( pDataValues->Name == "ScaleYNumerator" )
pDataValues->Value >>= m_nScaleYNumerator;
else if ( pDataValues->Name == "ScaleYDenominator" )
pDataValues->Value >>= m_nScaleYDenominator;
pDataValues++;
}
}
else if( rPropertyName == "SdrViewIsInEditMode" )
{
//#i77362 change notification for changes on additional shapes are missing
if( ! (rValue >>= m_bSdrViewIsInEditMode) )
throw lang::IllegalArgumentException( "Property 'SdrViewIsInEditMode' requires value of type sal_Bool", nullptr, 0 );
}
else
throw beans::UnknownPropertyException( "unknown property was tried to set to chart wizard", nullptr );
}
Any SAL_CALL ChartView::getPropertyValue( const OUString& rPropertyName )
{
if( rPropertyName != "Resolution" )
throw beans::UnknownPropertyException( "unknown property was tried to get from chart wizard", nullptr );
return Any(m_aPageResolution);
}
void SAL_CALL ChartView::addPropertyChangeListener(
const OUString& /* aPropertyName */, const Reference< beans::XPropertyChangeListener >& /* xListener */ )
{
OSL_FAIL("not implemented");
}
void SAL_CALL ChartView::removePropertyChangeListener(
const OUString& /* aPropertyName */, const Reference< beans::XPropertyChangeListener >& /* aListener */ )
{
OSL_FAIL("not implemented");
}
void SAL_CALL ChartView::addVetoableChangeListener( const OUString& /* PropertyName */, const Reference< beans::XVetoableChangeListener >& /* aListener */ )
{
OSL_FAIL("not implemented");
}
void SAL_CALL ChartView::removeVetoableChangeListener( const OUString& /* PropertyName */, const Reference< beans::XVetoableChangeListener >& /* aListener */ )
{
OSL_FAIL("not implemented");
}
// ____ XMultiServiceFactory ____
Reference< uno::XInterface > ChartView::createInstance( const OUString& aServiceSpecifier )
{
SolarMutexGuard aSolarGuard;
SdrModel* pModel = ( m_pDrawModelWrapper ? &m_pDrawModelWrapper->getSdrModel() : nullptr );
if ( pModel )
{
if ( aServiceSpecifier == "com.sun.star.drawing.DashTable" )
{
if ( !m_xDashTable.is() )
{
m_xDashTable = SvxUnoDashTable_createInstance( pModel );
}
return m_xDashTable;
}
else if ( aServiceSpecifier == "com.sun.star.drawing.GradientTable" )
{
if ( !m_xGradientTable.is() )
{
m_xGradientTable = SvxUnoGradientTable_createInstance( pModel );
}
return m_xGradientTable;
}
else if ( aServiceSpecifier == "com.sun.star.drawing.HatchTable" )
{
if ( !m_xHatchTable.is() )
{
m_xHatchTable = SvxUnoHatchTable_createInstance( pModel );
}
return m_xHatchTable;
}
else if ( aServiceSpecifier == "com.sun.star.drawing.BitmapTable" )
{
if ( !m_xBitmapTable.is() )
{
m_xBitmapTable = SvxUnoBitmapTable_createInstance( pModel );
}
return m_xBitmapTable;
}
else if ( aServiceSpecifier == "com.sun.star.drawing.TransparencyGradientTable" )
{
if ( !m_xTransGradientTable.is() )
{
m_xTransGradientTable = SvxUnoTransGradientTable_createInstance( pModel );
}
return m_xTransGradientTable;
}
else if ( aServiceSpecifier == "com.sun.star.drawing.MarkerTable" )
{
if ( !m_xMarkerTable.is() )
{
m_xMarkerTable = SvxUnoMarkerTable_createInstance( pModel );
}
return m_xMarkerTable;
}
}
return nullptr;
}
Reference< uno::XInterface > ChartView::createInstanceWithArguments( const OUString& ServiceSpecifier, const uno::Sequence< uno::Any >& Arguments )
{
OSL_ENSURE( Arguments.getLength(), "ChartView::createInstanceWithArguments: arguments are ignored" );
return createInstance( ServiceSpecifier );
}
uno::Sequence< OUString > ChartView::getAvailableServiceNames()
{
uno::Sequence< OUString > aServiceNames( 6 );
aServiceNames[0] = "com.sun.star.drawing.DashTable";
aServiceNames[1] = "com.sun.star.drawing.GradientTable";
aServiceNames[2] = "com.sun.star.drawing.HatchTable";
aServiceNames[3] = "com.sun.star.drawing.BitmapTable";
aServiceNames[4] = "com.sun.star.drawing.TransparencyGradientTable";
aServiceNames[5] = "com.sun.star.drawing.MarkerTable";
return aServiceNames;
}
OUString ChartView::dump()
{
#if HAVE_FEATURE_DESKTOP
// Used for unit tests and in chartcontroller only, no need to drag in this when cross-compiling
// for non-desktop
impl_updateView();
uno::Reference< drawing::XShapes > xShapes( m_xDrawPage, uno::UNO_QUERY_THROW );
sal_Int32 n = xShapes->getCount();
OUStringBuffer aBuffer;
for(sal_Int32 i = 0; i < n; ++i)
{
uno::Reference< drawing::XShapes > xShape(xShapes->getByIndex(i), uno::UNO_QUERY);
if(xShape.is())
{
XShapeDumper dumper;
OUString aString = XShapeDumper::dump(mxRootShape);
aBuffer.append(aString);
}
else
{
uno::Reference< drawing::XShape > xSingleShape(xShapes->getByIndex(i), uno::UNO_QUERY);
if(!xSingleShape.is())
continue;
XShapeDumper dumper;
OUString aString = XShapeDumper::dump(xSingleShape);
aBuffer.append(aString);
}
aBuffer.append("\n\n");
}
return aBuffer.makeStringAndClear();
#else
return OUString();
#endif
}
void ChartView::setViewDirty()
{
osl::ResettableMutexGuard aGuard(maTimeMutex);
m_bViewDirty = true;
}
IMPL_LINK_NOARG(ChartView, UpdateTimeBased, Timer *, void)
{
setViewDirty();
update();
}
void ChartView::createShapes2D( const awt::Size& rPageSize )
{
ShapeFactory* pShapeFactory = ShapeFactory::getOrCreateShapeFactory(m_xShapeFactory);
SolarMutexGuard aSolarGuard;
// todo: it would be nicer to just pass the page m_xDrawPage and format it,
// but the draw page does not support XPropertySet
formatPage( mrChartModel, rPageSize, mxRootShape, m_xShapeFactory );
CreateShapeParam2D aParam;
aParam.maRemainingSpace.X = 0;
aParam.maRemainingSpace.Y = 0;
aParam.maRemainingSpace.Width = rPageSize.Width;
aParam.maRemainingSpace.Height = rPageSize.Height;
//create the group shape for diagram and axes first to have title and legends on top of it
uno::Reference< XDiagram > xDiagram( mrChartModel.getFirstDiagram() );
OUString aDiagramCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM, OUString::number( 0 ) ) );//todo: other index if more than one diagram is possible
uno::Reference< drawing::XShapes > xDiagramPlusAxesPlusMarkHandlesGroup_Shapes(
pShapeFactory->createGroup2D(mxRootShape,aDiagramCID) );
aParam.mxMarkHandles = pShapeFactory->createInvisibleRectangle(
xDiagramPlusAxesPlusMarkHandlesGroup_Shapes, awt::Size(0,0));
ShapeFactory::setShapeName(aParam.mxMarkHandles, "MarkHandles");
aParam.mxPlotAreaWithAxes = pShapeFactory->createInvisibleRectangle(
xDiagramPlusAxesPlusMarkHandlesGroup_Shapes, awt::Size(0, 0));
ShapeFactory::setShapeName(aParam.mxPlotAreaWithAxes, "PlotAreaIncludingAxes");
aParam.mxDiagramWithAxesShapes = pShapeFactory->createGroup2D(xDiagramPlusAxesPlusMarkHandlesGroup_Shapes);
bool bAutoPositionDummy = true;
// create buttons
lcl_createButtons(mxRootShape, m_xShapeFactory, mrChartModel, aParam.maRemainingSpace);
lcl_createTitle(
TitleHelper::MAIN_TITLE, mxRootShape, m_xShapeFactory, mrChartModel,
aParam.maRemainingSpace, rPageSize, ALIGN_TOP, bAutoPositionDummy);
if (aParam.maRemainingSpace.Width <= 0 || aParam.maRemainingSpace.Height <= 0)
return;
lcl_createTitle(
TitleHelper::SUB_TITLE, mxRootShape, m_xShapeFactory, mrChartModel,
aParam.maRemainingSpace, rPageSize, ALIGN_TOP, bAutoPositionDummy );
if (aParam.maRemainingSpace.Width <= 0|| aParam.maRemainingSpace.Height <= 0)
return;
aParam.mpSeriesPlotterContainer.reset(new SeriesPlotterContainer(m_aVCooSysList));
aParam.mpSeriesPlotterContainer->initializeCooSysAndSeriesPlotter( mrChartModel );
if(maTimeBased.bTimeBased && maTimeBased.nFrame != 0)
{
SeriesPlottersType& rSeriesPlotter = aParam.mpSeriesPlotterContainer->getSeriesPlotterList();
size_t n = rSeriesPlotter.size();
for(size_t i = 0; i < n; ++i)
{
std::vector<VDataSeries*> aAllNewDataSeries = rSeriesPlotter[i]->getAllSeries();
std::vector< VDataSeries* >& rAllOldDataSeries =
maTimeBased.m_aDataSeriesList[i];
size_t m = std::min(aAllNewDataSeries.size(), rAllOldDataSeries.size());
for(size_t j = 0; j < m; ++j)
{
aAllNewDataSeries[j]->setOldTimeBased(
rAllOldDataSeries[j], (maTimeBased.nFrame % 60)/60.0);
}
}
}
lcl_createLegend(
LegendHelper::getLegend( mrChartModel ), mxRootShape, m_xShapeFactory, m_xCC,
aParam.maRemainingSpace, rPageSize, mrChartModel, aParam.mpSeriesPlotterContainer->getLegendEntryProviderList(),
lcl_getDefaultWritingModeFromPool( m_pDrawModelWrapper ) );
if (aParam.maRemainingSpace.Width <= 0 || aParam.maRemainingSpace.Height <= 0)
return;
if (!createAxisTitleShapes2D(aParam, rPageSize))
return;
bool bDummy = false;
bool bIsVertical = DiagramHelper::getVertical(xDiagram, bDummy, bDummy);
if (getAvailablePosAndSizeForDiagram(aParam, rPageSize, mrChartModel.getFirstDiagram()))
{
awt::Rectangle aUsedOuterRect = impl_createDiagramAndContent(aParam, rPageSize);
if (aParam.mxPlotAreaWithAxes.is())
{
aParam.mxPlotAreaWithAxes->setPosition(awt::Point(aUsedOuterRect.X, aUsedOuterRect.Y));
aParam.mxPlotAreaWithAxes->setSize(awt::Size(aUsedOuterRect.Width, aUsedOuterRect.Height));
}
//correct axis title position
awt::Rectangle aDiagramPlusAxesRect( aUsedOuterRect );
if (aParam.mbAutoPosTitleX)
changePositionOfAxisTitle(aParam.mpVTitleX.get(), ALIGN_BOTTOM, aDiagramPlusAxesRect, rPageSize);
if (aParam.mbAutoPosTitleY)
changePositionOfAxisTitle(aParam.mpVTitleY.get(), ALIGN_LEFT, aDiagramPlusAxesRect, rPageSize);
if (aParam.mbAutoPosTitleZ)
changePositionOfAxisTitle(aParam.mpVTitleZ.get(), ALIGN_Z, aDiagramPlusAxesRect, rPageSize);
if (aParam.mbAutoPosSecondTitleX)
changePositionOfAxisTitle(aParam.mpVTitleSecondX.get(), bIsVertical? ALIGN_RIGHT : ALIGN_TOP, aDiagramPlusAxesRect, rPageSize);
if (aParam.mbAutoPosSecondTitleY)
changePositionOfAxisTitle(aParam.mpVTitleSecondY.get(), bIsVertical? ALIGN_TOP : ALIGN_RIGHT, aDiagramPlusAxesRect, rPageSize);
}
//cleanup: remove all empty group shapes to avoid grey border lines:
lcl_removeEmptyGroupShapes( mxRootShape );
if(maTimeBased.bTimeBased && maTimeBased.nFrame % 60 == 0)
{
// create copy of the data for next frame
SeriesPlottersType& rSeriesPlotter = aParam.mpSeriesPlotterContainer->getSeriesPlotterList();
size_t n = rSeriesPlotter.size();
maTimeBased.m_aDataSeriesList.clear();
maTimeBased.m_aDataSeriesList.resize(n);
for(size_t i = 0; i < n; ++i)
{
std::vector<VDataSeries*> aAllNewDataSeries = rSeriesPlotter[i]->getAllSeries();
std::vector<VDataSeries*>& rAllOldDataSeries = maTimeBased.m_aDataSeriesList[i];
size_t m = aAllNewDataSeries.size();
for(size_t j = 0; j < m; ++j)
{
rAllOldDataSeries.push_back( aAllNewDataSeries[j]->
createCopyForTimeBased() );
}
}
maTimeBased.maTimer.Stop();
}
if(maTimeBased.bTimeBased && !maTimeBased.maTimer.IsActive())
{
maTimeBased.maTimer.SetTimeout(15);
maTimeBased.maTimer.SetInvokeHandler(LINK(this, ChartView, UpdateTimeBased));
maTimeBased.maTimer.Start();
}
}
bool ChartView::createAxisTitleShapes2D( CreateShapeParam2D& rParam, const css::awt::Size& rPageSize )
{
uno::Reference<XDiagram> xDiagram = mrChartModel.getFirstDiagram();
Reference< chart2::XChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
sal_Int32 nDimension = DiagramHelper::getDimension( xDiagram );
if( ChartTypeHelper::isSupportingMainAxis( xChartType, nDimension, 0 ) )
rParam.mpVTitleX = lcl_createTitle( TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION, mxRootShape, m_xShapeFactory, mrChartModel
, rParam.maRemainingSpace, rPageSize, ALIGN_BOTTOM, rParam.mbAutoPosTitleX );
if (rParam.maRemainingSpace.Width <= 0 ||rParam.maRemainingSpace.Height <= 0)
return false;
if( ChartTypeHelper::isSupportingMainAxis( xChartType, nDimension, 1 ) )
rParam.mpVTitleY = lcl_createTitle( TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION, mxRootShape, m_xShapeFactory, mrChartModel
, rParam.maRemainingSpace, rPageSize, ALIGN_LEFT, rParam.mbAutoPosTitleY );
if (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0)
return false;
if( ChartTypeHelper::isSupportingMainAxis( xChartType, nDimension, 2 ) )
rParam.mpVTitleZ = lcl_createTitle( TitleHelper::Z_AXIS_TITLE, mxRootShape, m_xShapeFactory, mrChartModel
, rParam.maRemainingSpace, rPageSize, ALIGN_RIGHT, rParam.mbAutoPosTitleZ );
if (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0)
return false;
bool bDummy = false;
bool bIsVertical = DiagramHelper::getVertical( xDiagram, bDummy, bDummy );
if( ChartTypeHelper::isSupportingSecondaryAxis( xChartType, nDimension ) )
rParam.mpVTitleSecondX = lcl_createTitle( TitleHelper::SECONDARY_X_AXIS_TITLE, mxRootShape, m_xShapeFactory, mrChartModel
, rParam.maRemainingSpace, rPageSize, bIsVertical? ALIGN_RIGHT : ALIGN_TOP, rParam.mbAutoPosSecondTitleX );
if (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0)
return false;
if( ChartTypeHelper::isSupportingSecondaryAxis( xChartType, nDimension ) )
rParam.mpVTitleSecondY = lcl_createTitle( TitleHelper::SECONDARY_Y_AXIS_TITLE, mxRootShape, m_xShapeFactory, mrChartModel
, rParam.maRemainingSpace, rPageSize, bIsVertical? ALIGN_TOP : ALIGN_RIGHT, rParam.mbAutoPosSecondTitleY );
if (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0)
return false;
return true;
}
} //namespace chart
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 &)
{
::chart::ChartModel *pChartModel = new ::chart::ChartModel(context);
return cppu::acquire(new ::chart::ChartView(context, *pChartModel));
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V560 A part of conditional expression is always true: nWritingMode != text::WritingMode2::PAGE.
↑ V547 Expression 'pItem' is always true.
↑ V1019 Compound assignment expression is used inside condition.
↑ V560 A part of conditional expression is always false: nWritingMode == text::WritingMode2::PAGE.
↑ V1019 Compound assignment expression is used inside condition.
↑ V547 Expression 'bRefreshAddInAllowed' is always true.
↑ V560 A part of conditional expression is always false: nWritingMode != - 1.
↑ V547 Expression 'bLinkToSource' is always true.
↑ V560 A part of conditional expression is always false: !bIncludeHiddenCells.
↑ V547 Expression 'bSortByXValues' is always false.
↑ V560 A part of conditional expression is always true: nWritingMode == - 1.
↑ V1019 Compound assignment expression is used inside condition.