Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/chart2/source/view/main/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 86 kB image not shown  

Quelle  ChartView.cxx   Sprache: C

 
/* -*- 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_feature_desktop.h>

#include "SeriesPlotterContainer.hxx"

#include <ChartView.hxx>
#include <chartview/DrawModelWrapper.hxx>
#include <Diagram.hxx>
#include <ChartType.hxx>
#include <DataSeries.hxx>
#include <NumberFormatterWrapper.hxx>
#include <VDiagram.hxx>
#include "VTitle.hxx"
#include "VButton.hxx"
#include <ShapeFactory.hxx>
#include <BaseCoordinateSystem.hxx>
#include <VCoordinateSystem.hxx>
#include <VSeriesPlotter.hxx>
#include <CommonConverters.hxx>
#include <TitleHelper.hxx>
#include <Legend.hxx>
#include <LegendHelper.hxx>
#include "VLegend.hxx"
#include <PropertyMapper.hxx>
#include <ChartModel.hxx>
#include <ChartTypeHelper.hxx>
#include <ScaleAutomatism.hxx>
#include <ObjectIdentifier.hxx>
#include <DiagramHelper.hxx>
#include <RelativePositionHelper.hxx>
#include <servicenames.hxx>
#include <Axis.hxx>
#include <AxisHelper.hxx>
#include "AxisUsage.hxx"
#include <AxisIndexDefines.hxx>
#include <BaseGFXHelper.hxx>
#include <DataSeriesHelper.hxx>
#include <DateHelper.hxx>
#include <ExplicitCategoriesProvider.hxx>
#include <defines.hxx>
#include <comphelper/dumpxmltostring.hxx>
#include <unonames.hxx>
#include <editeng/frmdiritem.hxx>
#include <editeng/eeitem.hxx>
#include <tools/globname.hxx>
#include <comphelper/fileformat.h>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/scopeguard.hxx>
#include <comphelper/servicehelper.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <rtl/math.hxx>
#include <unotools/streamwrap.hxx>
#include <svx/svdpage.hxx>
#include <svx/unopage.hxx>
#include <utility>
#include <vcl/svapp.hxx>
#include <osl/mutex.hxx>
#include <svx/unofill.hxx>
#include <drawinglayer/XShapeDumper.hxx>
#include <sfx2/objsh.hxx>

#include <time.h>

#include <com/sun/star/awt/Point.hpp>
#include <com/sun/star/chart/ChartAxisPosition.hpp>
#include <com/sun/star/chart/TimeUnit.hpp>
#include <com/sun/star/chart2/AxisType.hpp>
#include <com/sun/star/chart2/StackingDirection.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/GraphicExportFilter.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/XRefreshable.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/ctloptions.hxx>
#include <comphelper/classids.hxx>
#include <servicenames_charttypes.hxx>


#include <rtl/ustring.hxx>

#include <comphelper/diagnose_ex.hxx>
#include <tools/stream.hxx>

#include <memory>
#include <libxml/xmlwriter.h>

namespace chart {

using namespace ::com::sun::star;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::uno::Any;

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;

    rtl::Reference<SvxShapeRect> mxMarkHandles;
    rtl::Reference<SvxShapeRect> mxPlotAreaWithAxes;

    rtl::Reference<SvxShapeGroup> 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) {}
};



ChartView::ChartView(
        uno::Reference<uno::XComponentContext> xContext,
        ChartModel& rModel)
    : m_xCC(std::move(xContext))
    , mrChartModel(rModel)
    , 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 )
    {
        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 )
    {
        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();
}

// datatransfer::XTransferable
namespace
{
constexpr OUString lcl_aGDIMetaFileMIMEType(
    u"application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\""_ustr );
constexpr OUString lcl_aGDIMetaFileMIMETypeHighContrast(
    u"application/x-openoffice-highcontrast-gdimetafile;windows_formatname=\"GDIMetaFile\""_ustr );
// 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 > aFilterData{
        comphelper::makePropertyValue(u"ExportOnlyBackground"_ustr, false),
        comphelper::makePropertyValue(u"HighContrast"_ustr, bUseHighContrast),
        comphelper::makePropertyValue(u"Version"_ustr, sal_Int32(SOFFICE_FILEFORMAT_50)),
        comphelper::makePropertyValue(u"CurrentPage"_ustr, uno::Reference< uno::XInterface >( static_cast<cppu::OWeakObject*>(m_xDrawPage.get()), uno::UNO_QUERY )),
        //#i75867# poor quality of ole's alternative view with 3D scenes and zoomfactors besides 100%
        comphelper::makePropertyValue(u"ScaleXNumerator"_ustr, m_nScaleXNumerator),
        comphelper::makePropertyValue(u"ScaleXDenominator"_ustr, m_nScaleXDenominator),
        comphelper::makePropertyValue(u"ScaleYNumerator"_ustr, m_nScaleYNumerator),
        comphelper::makePropertyValue(u"ScaleYDenominator"_ustr, m_nScaleYDenominator)
    };

    uno::Sequence< beans::PropertyValue > aProps{
        comphelper::makePropertyValue(u"FilterName"_ustr, u"SVM"_ustr),
        comphelper::makePropertyValue(u"OutputStream"_ustr, xOutStream),
        comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData)
    };

    xExporter->setSourceDocument( m_xDrawPage );
    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 );
    rtl::Reference<utl::OStreamWrapper> pStreamWrapper = new utl::OStreamWrapper( aStream );

    this->getMetaFile( pStreamWrapper, bHighContrastMetaFile );

    pStreamWrapper->seek(0);
    sal_Int32 nBytesToRead = pStreamWrapper->available();
    uno::Sequence< sal_Int8 > aSeq( nBytesToRead );
    pStreamWrapper->readBytes( aSeq, nBytesToRead);
    aRet <<= aSeq;
    pStreamWrapper->closeInput();

    return aRet;
}
uno::Sequence< datatransfer::DataFlavor > SAL_CALL ChartView::getTransferDataFlavors()
{
    return
    {
        { lcl_aGDIMetaFileMIMEType, u"GDIMetaFile"_ustr, cppu::UnoType<uno::Sequence< sal_Int8 >>::get() },
        { lcl_aGDIMetaFileMIMETypeHighContrast, u"GDIMetaFile"_ustr, cppu::UnoType<uno::Sequence< sal_Int8 >>::get() }
    };
}
sal_Bool SAL_CALL ChartView::isDataFlavorSupported( const datatransfer::DataFlavor&&nbsp;aFlavor )
{
    return ( aFlavor.MimeType == lcl_aGDIMetaFileMIMEType ||
             aFlavor.MimeType == lcl_aGDIMetaFileMIMETypeHighContrast );
}

// lang::XServiceInfo

OUString SAL_CALL ChartView::getImplementationName()
{
    return 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 };
}

static ::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 rtl::Reference< Diagram >& 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 xDiagram->isPieOrDonutChart();
}

void lcl_setDefaultWritingMode( const std::shared_ptr< DrawModelWrapper >& pDrawModelWrapper, ChartModel& rModel)
{
    //get writing mode from parent document:
    if( !SvtCTLOptions::IsCTLFontEnabled() )
        return;

    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( u"PageStyles"_ustr ) )
                {
                    //draw/impress is parent document
                    uno::Reference< lang::XMultiServiceFactory > xFatcory( xParentProps, uno::UNO_QUERY );
                    if( xFatcory.is() )
                    {
                        uno::Reference< beans::XPropertySet > xDrawDefaults( xFatcory->createInstance( u"com.sun.star.drawing.Defaults"_ustr ), uno::UNO_QUERY );
                        if( xDrawDefaults.is() )
                            xDrawDefaults->getPropertyValue( u"WritingMode"_ustr ) >>= nWritingMode;
                    }
                }
                else
                {
                    uno::Reference< container::XNameAccess > xPageStyles( xStylesFamilies->getByName( u"PageStyles"_ustr ), 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( u"CLSID"_ustr ) >>= 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( u"WritingMode"_ustr ) >>= 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( u"PageStyleName"_ustr ) >>= 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( u"PageStyleName"_ustr ) >>= aPageStyle;
                                }
                            }
                            if(aPageStyle.isEmpty())
                                aPageStyle = "Standard";
                        }
                        else
                        {
                            //Calc is parent document
                            Reference< css::beans::XPropertySetInfo > xInfo = xParentProps->getPropertySetInfo();
                            if (xInfo->hasPropertyByName(u"PageStyle"_ustr))
                            {
                                xParentProps->getPropertyValue( u"PageStyle"_ustr ) >>= aPageStyle;
                            }
                            if(aPageStyle.isEmpty())
                                aPageStyle = "Default";
                        }
                        if( nWritingMode == -1 || nWritingMode == text::WritingMode2::PAGE )
                        {
                            uno::Reference< beans::XPropertySet > xPageStyle( xPageStyles->getByName( aPageStyle ), uno::UNO_QUERY );
                            Reference< css::beans::XPropertySetInfo > xInfo = xPageStyle->getPropertySetInfo();
                            if (xInfo->hasPropertyByName(u"WritingMode"_ustr))
                            {
                                if( xPageStyle.is() )
                                    xPageStyle->getPropertyValue( u"WritingMode"_ustr ) >>= nWritingMode;
                            }
                        }
                    }
                }
            }
        }
        if( nWritingMode != -1 && nWritingMode != text::WritingMode2::PAGE )
        {
            if( pDrawModelWrapper )
                pDrawModelWrapper->GetItemPool().SetUserDefaultItem(SvxFrameDirectionItem(static_cast<SvxFrameDirection>(nWritingMode), EE_PARA_WRITINGDIR) );
        }
    }
    catchconst uno::Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("chart2" );
    }
}

sal_Int16 lcl_getDefaultWritingModeFromPool( const std::shared_ptr<DrawModelWrapper>&&nbsp;pDrawModelWrapper )
{
    sal_Int16 nWritingMode = text::WritingMode2::LR_TB;
    if(!pDrawModelWrapper)
        return nWritingMode;

    const SfxPoolItem& rItem = pDrawModelWrapper->GetItemPool().GetUserOrPoolDefaultItem(EE_PARA_WRITINGDIR);
    nWritingMode
        = static_cast<sal_Int16>(static_cast<const SvxFrameDirectionItem&>(rItem).GetValue());
    return nWritingMode;
}

//end anonymous namespace

awt::Rectangle ChartView::impl_createDiagramAndContent( const CreateShapeParam2D&&nbsp;rParam, const awt::Size& rPageSize )
{
    //return the used rectangle
    awt::Rectangle aUsedOuterRect(rParam.maRemainingSpace.X, rParam.maRemainingSpace.Y, 0, 0);

    rtl::Reference< Diagram > xDiagram( mrChartModel.getFirstChartDiagram() );
    if( !xDiagram.is())
        return aUsedOuterRect;

    sal_Int32 nDimensionCount = xDiagram->getDimension();
    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() );
    auto& rSeriesPlotterList = rParam.mpSeriesPlotterContainer->getSeriesPlotterList();

    //create VAxis, so they can give necessary information for automatic scaling
    uno::Reference<util::XNumberFormatsSupplier> const xNumberFormatsSupplier(
            mrChartModel.getNumberFormatsSupplier());

    for (auto& rpVCooSys : rVCooSysList)
    {
        if (nDimensionCount == 3)
        {
            CuboidPlanePosition eLeftWallPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardLeftWall( xDiagram ) );
            CuboidPlanePosition eBackWallPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBackWall( xDiagram ) );
            CuboidPlanePosition eBottomPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBottom( xDiagram ) );
            rpVCooSys->set3DWallPositions( eLeftWallPos, eBackWallPos, eBottomPos );
        }
        rpVCooSys->createVAxisList(&mrChartModel, rPageSize, rParam.maRemainingSpace,
            rParam.mbUseFixedInnerSize, rSeriesPlotterList, getComponentContext());
    }

    // - 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();

    rtl::Reference<SvxShapeGroupAnyD> xSeriesTargetInFrontOfAxis;
    rtl::Reference<SvxShapeGroupAnyD> xSeriesTargetBehindAxis;
    VDiagram aVDiagram(xDiagram, aPreferredAspectRatio, nDimensionCount);
    {//create diagram
        aVDiagram.init(rParam.mxDiagramWithAxesShapes);
        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 (!rParam.mbUseFixedInnerSize)
            aVDiagram.reduceToMinimumSize();
    }

    rtl::Reference<SvxShapeGroup> xTextTargetShapes =
        ShapeFactory::createGroup2D(rParam.mxDiagramWithAxesShapes);

    // - create axis and grids for all coordinate systems

    //init all coordinate systems
    for (auto& rpVCooSys : rVCooSysList)
    {
        rpVCooSys->initPlottingTargets(xSeriesTargetInFrontOfAxis, xTextTargetShapes, xSeriesTargetBehindAxis);

        rpVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
            createTransformationSceneToScreen( aVDiagram.getCurrentRectangle() ) ));

        rpVCooSys->initVAxisInList();
    }

    //calculate resulting size respecting axis label layout and fontscaling

    rtl::Reference<SvxShapeGroup> xBoundingShape(rParam.mxDiagramWithAxesShapes);
    ::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
    bool bIsPieOrDonut = lcl_IsPieOrDonut(xDiagram);
    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() ) ));

            // Need to re-adjust again if the labels have changed height because of
            // text can break. Ideally this shouldn't be needed, but the chart height
            // isn't readjusted otherwise.
            pVCooSys->createAxesLabels();
            aConsumedOuterRect = ShapeFactory::getRectangleOfShape(*xBoundingShape);
            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 (auto& rpVCooSys : rVCooSysList)
    {
        rpVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
            createTransformationSceneToScreen( aVDiagram.getCurrentRectangle() ) ));

        rpVCooSys->createAxesShapes();
        rpVCooSys->createGridShapes();
    }

    // - create data series for all charttypes
    m_bPointsWereSkipped = false;
    forconst std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
    {
        VSeriesPlotter* pSeriesPlotter = aPlotter.get();
        rtl::Reference<SvxShapeGroupAnyD> 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,OUString() );
        pSeriesPlotter->setPageReferenceSize( rPageSize );
        VCoordinateSystem* pVCooSys = SeriesPlotterContainer::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 );
        }
        // Do not allow to move data labels in case of pie or donut chart, yet!
        pSeriesPlotter->setPieLabelsAllowToMove(!bIsPieOrDonut);
        // use the pagesize as remaining space if we have a fixed inner size
        if( rParam.mbUseFixedInnerSize )
            aAvailableOuterRect = BaseGFXHelper::makeRectangle(awt::Rectangle(0, 0, rPageSize.Width, rPageSize.Height));
        // set the available space for data labels to avoid moving out from chart area
        pSeriesPlotter->setAvailableOuterRect(aAvailableOuterRect);
        pSeriesPlotter->createShapes();
        m_bPointsWereSkipped = m_bPointsWereSkipped || pSeriesPlotter->PointsWereSkipped();
    }

    //recreate all with corrected sizes if requested
    if( bIsPieOrDonut )
    {
        m_bPointsWereSkipped = false;

        aConsumedOuterRect = 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 (auto& rpVCooSys : rVCooSysList)
        {
            auto aMatrix = createTransformationSceneToScreen(aNewInnerRect);
            rpVCooSys->setTransformationSceneToScreen(B3DHomMatrixToHomogenMatrix(aMatrix));
        }

        // - create data series for all charttypes
        for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
        {
            VCoordinateSystem* pVCooSys = SeriesPlotterContainer::getCooSysForPlotter( rVCooSysList, aPlotter.get() );
            if(nDimensionCount==2)
                aPlotter->setTransformationSceneToScreen( pVCooSys->getTransformationSceneToScreen() );
            // Now we can move data labels in case of pie or donut chart!
            aPlotter->setPieLabelsAllowToMove(bIsPieOrDonut);
            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( u"PlotAreaExcludingAxes"_ustr );
        else
        {
            ::basegfx::B2IRectangle aConsumedInnerRect = aVDiagram.getCurrentRectangle();
            m_aResultingDiagramRectangleExcludingAxes = BaseGFXHelper::toAwtRectangle(aConsumedInnerRect);
        }
    }
    else
    {
        if (rParam.mbUseFixedInnerSize)
            m_aResultingDiagramRectangleExcludingAxes = rParam.maRemainingSpace;
        else
        {
            ::basegfx::B2IRectangle aConsumedInnerRect = aVDiagram.getCurrentRectangle();
            m_aResultingDiagramRectangleExcludingAxes = BaseGFXHelper::toAwtRectangle(aConsumedInnerRect);
        }
    }

    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;
        xDiagram->getPropertyValue(u"PosSizeExcludeAxes"_ustr) >>= 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(
                     const rtl::Reference< Axis >& xAxis
                     , ExplicitScaleData&  rExplicitScale
                     , ExplicitIncrementData& rExplicitIncrement )
{
    SolarMutexGuard aSolarGuard;

    impl_updateView();

    if(!xAxis.is())
        return false;

    rtl::Reference< BaseCoordinateSystem > xCooSys = AxisHelper::getCoordinateSystemOfAxis(xAxis, mrChartModel.getFirstChartDiagram() );
    const VCoordinateSystem* pVCooSys = SeriesPlotterContainer::findInCooSysList(m_aVCooSysList, xCooSys);
    if(!pVCooSys)
        return false;

    sal_Int32 nDimensionIndex=-1;
    sal_Int32 nAxisIndex=-1;
    if( !AxisHelper::getIndicesForAxis( xAxis, xCooSys, nDimensionIndex, nAxisIndex ) )
        return false;

    rExplicitScale = pVCooSys->getExplicitScale(nDimensionIndex,nAxisIndex);
    rExplicitIncrement = pVCooSys->getExplicitIncrement(nDimensionIndex,nAxisIndex);
    if( !rExplicitScale.m_bShiftedCategoryPosition )
        return true;

    //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;
}

SdrPage* ChartView::getSdrPage()
{
    if(m_xDrawPage)
        return m_xDrawPage->GetSdrPage();

    return nullptr;
}

rtl::Reference< SvxShape > ChartView::getShapeForCID( const OUString& rObjectCID )
{
    SolarMutexGuard aSolarGuard;
    SdrObject* pObj = DrawModelWrapper::getNamedSdrObject( rObjectCID, this->getSdrPage() );
    if( !pObj )
        return nullptr;

    uno::Reference< drawing::XShape > xShape = pObj->getUnoShape();
    rtl::Reference<SvxShape> xShape2 = dynamic_cast<SvxShape*>(xShape.get());
    assert(xShape2 || !xShape);
    return xShape2;
}

awt::Rectangle ChartView::getDiagramRectangleExcludingAxes()
{
    impl_updateView();
    return m_aResultingDiagramRectangleExcludingAxes;
}

awt::Rectangle ChartView::getRectangleOfObject( const OUString& rObjectCID, bool bSnapRect )
{
    impl_updateView();

    awt::Rectangle aRet;
    rtl::Reference< SvxShape > 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;
            SdrObject* pRootSdrObject = xShape->GetSdrObject();
            if( pRootSdrObject )
            {
                SdrObjList* pRootList = pRootSdrObject->GetSubList();
                if( pRootList )
                {
                    OUString aShapeName = u"MarkHandles"_ustr;
                    if( eObjectType == OBJECTTYPE_DIAGRAM )
                        aShapeName = "PlotAreaIncludingAxes";
                    SdrObject* pShape = DrawModelWrapper::getNamedSdrObject( aShapeName, pRootList );
                    if( pShape )
                    {
                        xShape = dynamic_cast<SvxShape*>(pShape->getUnoShape().get());
                        assert(xShape);
                    }
                }
            }
        }

        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
            SdrObject* pSdrObject = xShape->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
{

constexpr double constPageLayoutDistancePercentage = 0.02;
constexpr sal_Int32 constPageLayoutFixedDistance = 350;

bool getAvailablePosAndSizeForDiagram(
    CreateShapeParam2D& rParam, const awt::Size & rPageSize, rtl::Reference<Diagram> const& xDiagram)
{
    rParam.mbUseFixedInnerSize = false;

    //@todo: we need a size dependent on the axis labels
    rtl::Reference<ChartType> xChartType;
    if (xDiagram)
        xChartType = xDiagram->getChartTypeByIndex(0);

    sal_Int32 nXDistance = sal_Int32(rPageSize.Width * constPageLayoutDistancePercentage);
    sal_Int32 nYDistance = sal_Int32(rPageSize.Height * constPageLayoutDistancePercentage);

    // Only pie chart uses fixed size margins
    if (xChartType.is() && xChartType->getChartType() == CHART2_SERVICE_NAME_CHARTTYPE_PIE)
    {
        nXDistance = constPageLayoutFixedDistance;
        nYDistance = constPageLayoutFixedDistance;
    }

    rParam.maRemainingSpace.X += nXDistance;
    rParam.maRemainingSpace.Width -= 2*nXDistance;
    rParam.maRemainingSpace.Y += nYDistance;
    rParam.maRemainingSpace.Height -= 2*nYDistance;

    bool bPosSizeExcludeAxes = false;
    if( xDiagram.is() )
        xDiagram->getPropertyValue( u"PosSizeExcludeAxes"_ustr ) >>= bPosSizeExcludeAxes;

    //size:
    css::chart2::RelativeSize aRelativeSize;
    if( xDiagram.is() && (xDiagram->getPropertyValue( u"RelativeSize"_ustr )>>=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;
    }

    if (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0)
        return false;

    //position:
    chart2::RelativePosition aRelativePosition;
    if( xDiagram.is() && (xDiagram->getPropertyValue( u"RelativePosition"_ustr )>>=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 class 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 * constPageLayoutDistancePercentage);
    sal_Int32 nXDistance = static_cast<sal_Int32>(rPageSize.Width * constPageLayoutDistancePercentage);
    switch (eAlignment)
    {
    case TitleAlignment::ALIGN_TOP:
        aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width/2
                                    , rDiagramPlusAxesRect.Y - aTitleSize.Height/2  - nYDistance );
        break;
    case TitleAlignment::ALIGN_BOTTOM:
        aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width/2
                                    , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height + aTitleSize.Height/2  + nYDistance );
        break;
    case TitleAlignment::ALIGN_LEFT:
        aNewPosition = awt::Point( rDiagramPlusAxesRect.X - aTitleSize.Width/2 - nXDistance
                                    , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height/2 );
        break;
    case TitleAlignment::ALIGN_RIGHT:
        aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width + aTitleSize.Width/2 + nXDistance
                                    , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height/2 );
        break;
    case TitleAlignment::ALIGN_Z:
        aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width + aTitleSize.Width/2 + nXDistance
                                    , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height - aTitleSize.Height/2 );
       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 rtl::Reference<SvxShapeGroupAnyD>& xPageShapes
                , 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 = constPageLayoutDistancePercentage;
    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
    }

    rtl::Reference< Title > xTitle( TitleHelper::getTitle( eType, rModel ) );
    OUString aCompleteString = TitleHelper::getCompleteString(xTitle);
    if (aCompleteString.isEmpty() || !VTitle::isVisible(xTitle))
        return apVTitle;

    //create title
    awt::Size aTextMaxWidth(rPageSize.Width, rPageSize.Height);
    bool bYAxisTitle = false;
    if (eType == TitleHelper::MAIN_TITLE || eType == TitleHelper::SUB_TITLE)
    {
        aTextMaxWidth.Width = static_cast<sal_Int32>(rPageSize.Width * 0.8);
        aTextMaxWidth.Height = static_cast<sal_Int32>(rPageSize.Height * 0.5);
    }
    else if (eType == TitleHelper::X_AXIS_TITLE || eType == TitleHelper::SECONDARY_X_AXIS_TITLE
             || eType == TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION)
    {
        aTextMaxWidth.Width = static_cast<sal_Int32>(rPageSize.Width * 0.8);
        aTextMaxWidth.Height = static_cast<sal_Int32>(rPageSize.Height * 0.2);
    }
    else if (eType == TitleHelper::Y_AXIS_TITLE || eType == TitleHelper::SECONDARY_Y_AXIS_TITLE
             || eType == TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION)
    {
        aTextMaxWidth.Width = static_cast<sal_Int32>(rPageSize.Width * 0.2);
        aTextMaxWidth.Height = static_cast<sal_Int32>(rPageSize.Height * 0.8);
        bYAxisTitle = true;
    }
    apVTitle = std::make_shared<VTitle>(xTitle);
    OUString aCID = ObjectIdentifier::createClassifiedIdentifierForObject(xTitle, &rModel);
    apVTitle->init(xPageShapes, aCID);
    apVTitle->createShapes(awt::Point(0, 0), rPageSize, aTextMaxWidth, bYAxisTitle);
    awt::Size aTitleUnrotatedSize = apVTitle->getUnrotatedSize();
    awt::Size aTitleSize = apVTitle->getFinalSize();

    //position
    rbAutoPosition = true;
    awt::Point aNewPosition(0,0);
    chart2::RelativePosition aRelativePosition;
    if (xTitle.is() && (xTitle->getPropertyValue(u"RelativePosition"_ustr) >>= 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 );

        const bool bInfiniteY = std::isinf(aRelativePosition.Secondary);
        if (bInfiniteY)
        {
            SAL_WARN("chart2""infinite aRelativePosition.Secondary position, using ALIGN_BOTTOM");
            aNewPosition.Y = rRemainingSpace.Y + rRemainingSpace.Height - aTitleSize.Height/2 - nYDistance;
        }

        const bool bInfiniteX = std::isinf(aRelativePosition.Primary);
        if (bInfiniteX)
        {
            SAL_WARN("chart2""infinite aRelativePosition.Primary position, using ALIGN_RIGHT");
            aNewPosition.X = rRemainingSpace.X + rRemainingSpace.Width - aTitleSize.Width/2 - nXDistance;
        }
    }
    else //auto position
    {
        switch( eAlignment )
        {
        case TitleAlignment::ALIGN_TOP:
            aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width/2
                                     , rRemainingSpace.Y + aTitleSize.Height/2 + nYDistance );
            break;
        case TitleAlignment::ALIGN_BOTTOM:
            aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width/2
                                     , rRemainingSpace.Y + rRemainingSpace.Height - aTitleSize.Height/2 - nYDistance );
            break;
        case TitleAlignment::ALIGN_LEFT:
            aNewPosition = awt::Point( rRemainingSpace.X + aTitleSize.Width/2 + nXDistance
                                     , rRemainingSpace.Y + rRemainingSpace.Height/2 );
            break;
        case TitleAlignment::ALIGN_RIGHT:
            aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width - aTitleSize.Width/2 - nXDistance
                                     , rRemainingSpace.Y + rRemainingSpace.Height/2 );
            break;
        case TitleAlignment::ALIGN_Z:
            break;

        }
    }
    apVTitle->changePosition( aNewPosition );

    //remaining space
    switch( eAlignment )
    {
        case TitleAlignment::ALIGN_TOP:
            // Push the remaining space down from top.
            rRemainingSpace.Y += ( aTitleSize.Height + nYDistance );
            rRemainingSpace.Height -= ( aTitleSize.Height + nYDistance );
            break;
        case TitleAlignment::ALIGN_BOTTOM:
            // Push the remaining space up from bottom.
            rRemainingSpace.Height -= ( aTitleSize.Height + nYDistance );
            break;
        case TitleAlignment::ALIGN_LEFT:
            // Push the remaining space to the right from left edge.
            rRemainingSpace.X += ( aTitleSize.Width + nXDistance );
            rRemainingSpace.Width -= ( aTitleSize.Width + nXDistance );
            break;
        case TitleAlignment::ALIGN_RIGHT:
            // Push the remaining space to the left from right edge.
            rRemainingSpace.Width -= ( aTitleSize.Width + nXDistance );
            break;
        case TitleAlignment::ALIGN_Z:
            break;
    }

    return apVTitle;
}

bool lcl_createLegend( const rtl::Reference< Legend > & xLegend
                   , const rtl::Reference<SvxShapeGroupAnyD>& xPageShapes
                   , const uno::Reference< uno::XComponentContext > & xContext
                   , awt::Rectangle & rRemainingSpace
                   , const awt::Size & rPageSize
                   , ChartModel& rModel
                   , std::vector< LegendEntryProvider* >&& rLegendEntryProviderList
                   , sal_Int16 nDefaultWritingMode )
{
    if (!VLegend::isVisible(xLegend))
        return false;

    awt::Size rDefaultLegendSize;
    VLegend aVLegend( xLegend, xContext, std::move(rLegendEntryProviderList),
            xPageShapes, rModel);
    aVLegend.setDefaultWritingMode( nDefaultWritingMode );
    aVLegend.createShapes( awt::Size( rRemainingSpace.Width, rRemainingSpace.Height ),
                           rPageSize, rDefaultLegendSize );
    aVLegend.changePosition( rRemainingSpace, rPageSize, rDefaultLegendSize );
    return true;
}

void lcl_createButtons(const rtl::Reference<SvxShapeGroupAnyD>& xPageShapes,
                       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

    tools::Long x = 0;

    if (xPivotTableDataProvider->getPageFields().hasElements())
    {
        x = 0;

        const css::uno::Sequence<chart2::data::PivotTableFieldEntry> aPivotFieldEntries = xPivotTableDataProvider->getPageFields();
        for (css::chart2::data::PivotTableFieldEntry const & rPageFieldEntry : aPivotFieldEntries)
        {
            VButton aButton;
            aButton.init(xPageShapes);
            awt::Point aNewPosition(rRemainingSpace.X + x + 100, rRemainingSpace.Y + 100);
            sal_Int32 nDimensionIndex = rPageFieldEntry.DimensionIndex;
            OUString aFieldOutputDescription = xPivotTableDataProvider->getFieldOutputDescription(nDimensionIndex);
            aButton.setLabel(rPageFieldEntry.Name + " | " + aFieldOutputDescription);
            aButton.setCID("FieldButton.Page." + OUString::number(nDimensionIndex));
            aButton.setPosition(aNewPosition);
            aButton.setSize(aSize);
            if (rPageFieldEntry.HasHiddenMembers)
                aButton.setArrowColor(Color(0x0000FF));

            aButton.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())
        return;

    x = 200;
    const css::uno::Sequence<chart2::data::PivotTableFieldEntry> aPivotFieldEntries = xPivotTableDataProvider->getRowFields();
    for (css::chart2::data::PivotTableFieldEntry const & rRowFieldEntry : aPivotFieldEntries)
    {
        VButton aButton;
        aButton.init(xPageShapes);
        awt::Point aNewPosition(rRemainingSpace.X + x + 100,
                                rRemainingSpace.Y + rRemainingSpace.Height - aSize.Height - 100);
        aButton.setLabel(rRowFieldEntry.Name);
        aButton.setCID("FieldButton.Row." + OUString::number(rRowFieldEntry.DimensionIndex));
        aButton.setPosition(aNewPosition);
        aButton.setSize(aSize);
        if ( rRowFieldEntry.Name == "Data" )
        {
            aButton.setBGColor( Color(0x00F6F6F6) );
            aButton.showArrow( false );
        }
        else if (rRowFieldEntry.HasHiddenMembers)
            aButton.setArrowColor(Color(0x0000FF));
        aButton.createShapes(xModelPage);
        x += aSize.Width + 100;
    }
    rRemainingSpace.Height -= (aSize.Height + 100 + 100);
}

void formatPage(
      ChartModel& rChartModel
    , const awt::Size& rPageSize
    , const rtl::Reference<SvxShapeGroupAnyD>& xTarget
    )
{
    try
    {
        uno::Reference< beans::XPropertySet > xModelPage( rChartModel.getPageBackground());
        if( ! xModelPage.is())
            return;

        //format page
        tPropertyNameValueMap aNameValueMap;
        PropertyMapper::getValueMap( aNameValueMap, PropertyMapper::getPropertyNameMapForFillAndLineProperties(), xModelPage );

        OUString aCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, u"" ) );
        aNameValueMap.emplace( "Name", uno::Any( aCID ) ); //CID OUString

        tNameSequence aNames;
        tAnySequence aValues;
        PropertyMapper::getMultiPropertyListsFromValueMap( aNames, aValues, aNameValueMap );

        ShapeFactory::createRectangle(
            xTarget, rPageSize, awt::Point(0, 0), aNames, aValues);
    }
    catchconst uno::Exception & )
    {
        DBG_UNHANDLED_EXCEPTION("chart2" );
    }
}

void lcl_removeEmptyGroupShapes( const SdrObject& rParent )
{
    SdrObjList* pObjList = rParent.getChildrenOfSdrObject();
    if (!pObjList || pObjList->GetObjCount() == 0)
        return;

    //iterate from back!
    for(auto nIdx = static_cast<sal_Int32>(pObjList->GetObjCount() - 1); nIdx >= 0; --nIdx)
    {
        SdrObject* pChildSdrObject = pObjList->GetObj(nIdx);
        SdrObjList* pChildObjList = pChildSdrObject->getChildrenOfSdrObject();
        if (!pChildObjList)
            continue;
        if (pChildObjList->GetObjCount() == 0)
        {
            //remove empty group shape
            pObjList->NbcRemoveObject(nIdx);
        }
        else
            lcl_removeEmptyGroupShapes(*pChildSdrObject);
    }
}

}

void ChartView::impl_refreshAddIn()
{
    if( !m_bRefreshAddIn )
        return;

    uno::Reference< beans::XPropertySet > xProp( static_cast< ::cppu::OWeakObject* >( &mrChartModel ), uno::UNO_QUERY );
    if( !xProp.is())
        return;

    try
    {
        uno::Reference< util::XRefreshable > xAddIn;
        xProp->getPropertyValue( u"AddIn"_ustr ) >>= xAddIn;
        if( xAddIn.is() )
        {
            bool bRefreshAddInAllowed = true;
            xProp->getPropertyValue( u"RefreshAddInAllowed"_ustr ) >>= bRefreshAddInAllowed;
            if( bRefreshAddInAllowed )
                xAddIn->refresh();
        }
    }
    catchconst uno::Exception& )
    {
        TOOLS_WARN_EXCEPTION("chart2""" );
    }
}

void ChartView::createShapes()
{
    SolarMutexGuard aSolarGuard;

    std::unique_lock 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 )
    {
        // #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 );

    if(!mxRootShape.is())
        mxRootShape = ShapeFactory::getOrCreateChartRootShape( m_xDrawPage );

    SdrPage* pPage = 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");
    }
    ShapeFactory::setPageSize(mxRootShape, aPageSize);

    createShapes2D(aPageSize);

    // #i12587# support for shapes in chart
    if ( m_pDrawModelWrapper )
    {
        m_pDrawModelWrapper->getSdrModel().EnableUndo( true );
    }

    if(maTimeBased.bTimeBased)
    {
        maTimeBased.nFrame++;
    }
}

// util::XEventListener (base of XCloseListener)
void SAL_CALL ChartView::disposing( const lang::EventObject& /* rSource */ )
{
}

namespace
{
// Disables setting the chart's modified state, as well as its parent's (if exists).
// Painting a chart must not set these states.
struct ChartModelDisableSetModified
{
    ChartModel& mrChartModel;
    SfxObjectShell* mpParentShell;
    bool mbWasUnmodified;
    ChartModelDisableSetModified(ChartModel& rChartModel)
        : mrChartModel(rChartModel)
        , mpParentShell(SfxObjectShell::GetShellFromComponent(rChartModel.getParent()))
        , mbWasUnmodified(!rChartModel.isModified())
    {
        if (mpParentShell && mpParentShell->IsEnableSetModified())
            mpParentShell->EnableSetModified(false);
        else
            mpParentShell = nullptr;
    }
    ~ChartModelDisableSetModified()
    {
        if (mbWasUnmodified && mrChartModel.isModified())
            mrChartModel.setModified(false);
        if (mpParentShell)
            mpParentShell->EnableSetModified(true);
    }
};
}

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 )
        return;

    m_bInViewUpdate = true;
    //bool bOldRefreshAddIn = m_bRefreshAddIn;
    //m_bRefreshAddIn = false;
    try
    {
        impl_notifyModeChangeListener(u"invalid"_ustr);

        //prepare draw model
        {
            SolarMutexGuard aSolarGuard;
            m_pDrawModelWrapper->lockControllers();
        }

        // Rendering the chart must not set its (or its parent) modified status
        ChartModelDisableSetModified dontSetModified(mrChartModel);

        //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;
    }
    catchconst uno::Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("chart2" );
        m_bViewDirty = m_bViewUpdatePending;
        m_bViewUpdatePending = false;
        m_bInViewUpdate = false;
    }

    {
        SolarMutexGuard aSolarGuard;
        m_pDrawModelWrapper->unlockControllers();
    }

    impl_notifyModeChangeListener(u"valid"_ustr);

    //m_bRefreshAddIn = bOldRefreshAddIn;
}

// ____ XModifyListener ____
void SAL_CALL ChartView::modified( const lang::EventObject& /* aEvent */ )
{
    m_bViewDirty = true;
    if( m_bInViewUpdate )
        m_bViewUpdatePending = true;

    impl_notifyModeChangeListener(u"dirty"_ustr);
}

//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;
            }
        }
    }

    if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
        return;
    const SdrHint* pSdrHint = static_castconst SdrHint* >(&rHint);

    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
    {
        std::unique_lock g(m_aMutex);
        if( m_aModeChangeListeners.getLength(g) )
        {
            util::ModeChangeEvent aEvent( static_cast< uno::XWeak* >( this ), rNewMode );
            m_aModeChangeListeners.notifyEach( g, &css::util::XModeChangeListener::modeChanged, aEvent);
        }
    }
    catchconst uno::Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("chart2");
    }
}

// ____ XModeChangeBroadcaster ____

void SAL_CALL ChartView::addModeChangeListener( const uno::Reference< util::XModeChangeListener >& xListener )
{
    std::unique_lock g(m_aMutex);
    m_aModeChangeListeners.addInterface(g, xListener );
}
void SAL_CALL ChartView::removeModeChangeListener( const uno::Reference< util::XModeChangeListener >& xListener )
{
    std::unique_lock g(m_aMutex);
    m_aModeChangeListeners.removeInterface(g, 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 is 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, truefalse ) )
        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;
}

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=96 G=94

¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.