    /* -*- 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 <ChartController.hxx>
    #include <PositionAndSizeHelper.hxx>
    #include <ObjectIdentifier.hxx>
    #include <ChartWindow.hxx>
    #include <ResId.hxx>
    #include <CommonConverters.hxx>
    #include <ChartModel.hxx>
    #include <ChartModelHelper.hxx>
    #include <DiagramHelper.hxx>
    #include <TitleHelper.hxx>
    #include "UndoGuard.hxx"
    #include <ControllerLockGuard.hxx>
    #include <ObjectNameProvider.hxx>
    #include <strings.hrc>
    #include "DragMethod_PieSegment.hxx"
    #include "DragMethod_RotateDiagram.hxx"
    #include <ObjectHierarchy.hxx>
    #include <chartview/ExplicitValueProvider.hxx>
    #include <RelativePositionHelper.hxx>
    #include <chartview/DrawModelWrapper.hxx>
    #include <RegressionCurveHelper.hxx>
    #include <StatisticsHelper.hxx>
    #include <DataSeriesHelper.hxx>
    #include <ContainerHelper.hxx>
    #include <AxisHelper.hxx>
    #include <LegendHelper.hxx>
    #include <servicenames_charttypes.hxx>
    #include "DrawCommandDispatch.hxx"
    #include <PopupRequest.hxx>

    #include <com/sun/star/chart2/RelativePosition.hpp>
    #include <com/sun/star/chart2/RelativeSize.hpp>
    #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
    #include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
    #include <com/sun/star/chart2/XChartDocument.hpp>

    #include <com/sun/star/awt/PopupMenuDirection.hpp>
    #include <com/sun/star/frame/DispatchHelper.hpp>
    #include <com/sun/star/frame/FrameSearchFlag.hpp>
    #include <com/sun/star/frame/XPopupMenuController.hpp>
    #include <com/sun/star/util/XUpdatable.hpp>
    #include <com/sun/star/awt/Rectangle.hpp>
    #include <com/sun/star/qa/XDumper.hpp>

    #include <comphelper/lok.hxx>
    #include <comphelper/propertysequence.hxx>
    #include <comphelper/propertyvalue.hxx>

    #include <toolkit/awt/vclxmenu.hxx>

    #include <sfx2/viewsh.hxx>
    #include <sfx2/ipclient.hxx>
    #include <svx/svxids.hrc>
    #include <svx/ActionDescriptionProvider.hxx>
    #include <svx/obj3d.hxx>
    #include <svx/scene3d.hxx>
    #include <svx/svddrgmt.hxx>
    #include <vcl/svapp.hxx>
    #include <vcl/settings.hxx>
    #include <vcl/weld.hxx>
    #include <rtl/math.hxx>
    #include <svtools/acceleratorexecute.hxx>
    #include <tools/diagnose_ex.h>
    #include <sal/log.hxx>

    #define DRGPIX    2     // Drag MinMove in Pixel

    using namespace ::com::sun::star;
    using namespace ::com::sun::star::chart2;
    using ::com::sun::star::uno::Reference;

    namespace chart
    {

    namespace
    {
    bool lcl_GrowAndShiftLogic(
        RelativePosition &  rInOutRelPos,
        RelativeSize &      rInOutRelSize,
        const awt::Size &   rRefSize,
        double              fGrowLogicX,
        double              fGrowLogicY )
    {
        if( rRefSize.Width == 0 ||
            rRefSize.Height == 0 )
            return false;

        double fRelativeGrowX = fGrowLogicX / rRefSize.Width;
        double fRelativeGrowY = fGrowLogicY / rRefSize.Height;

        return ::chart::RelativePositionHelper::centerGrow(
            rInOutRelPos, rInOutRelSize,
            fRelativeGrowX, fRelativeGrowY );
    }

    bool lcl_MoveObjectLogic(
        RelativePosition &  rInOutRelPos,
        RelativeSize const & rObjectSize,
        const awt::Size &   rRefSize,
        double              fShiftLogicX,
        double              fShiftLogicY )
    {
        if( rRefSize.Width == 0 ||
            rRefSize.Height == 0 )
            return false;

        double fRelativeShiftX = fShiftLogicX / rRefSize.Width;
        double fRelativeShiftY = fShiftLogicY / rRefSize.Height;

        return ::chart::RelativePositionHelper::moveObject(
            rInOutRelPos, rObjectSize,
            fRelativeShiftX, fRelativeShiftY );
    }

    void lcl_insertMenuCommand(
        const uno::Reference< awt::XPopupMenu > & xMenu,
        sal_Int16 nId, const OUString & rCommand )
    {
        xMenu->insertItem( nId, "", 0, -1 );
        xMenu->setCommand( nId, rCommand );
    }

    OUString lcl_getFormatCommandForObjectCID( const OUString& rCID )
    {
        OUString aDispatchCommand( ".uno:FormatSelection" );

        ObjectType eObjectType = ObjectIdentifier::getObjectType( rCID );

        switch(eObjectType)
        {
            case OBJECTTYPE_DIAGRAM:
            case OBJECTTYPE_DIAGRAM_WALL:
                aDispatchCommand = ".uno:FormatWall";
                break;
            case OBJECTTYPE_DIAGRAM_FLOOR:
                aDispatchCommand = ".uno:FormatFloor";
                break;
            case OBJECTTYPE_PAGE:
                aDispatchCommand = ".uno:FormatChartArea";
                break;
            case OBJECTTYPE_LEGEND:
                aDispatchCommand = ".uno:FormatLegend";
                break;
            case OBJECTTYPE_TITLE:
                aDispatchCommand = ".uno:FormatTitle";
                break;
            case OBJECTTYPE_LEGEND_ENTRY:
                aDispatchCommand = ".uno:FormatDataSeries";
                break;
            case OBJECTTYPE_AXIS:
            case OBJECTTYPE_AXIS_UNITLABEL:
                aDispatchCommand = ".uno:FormatAxis";
                break;
            case OBJECTTYPE_GRID:
                aDispatchCommand = ".uno:FormatMajorGrid";
                break;
            case OBJECTTYPE_SUBGRID:
                aDispatchCommand = ".uno:FormatMinorGrid";
                break;
            case OBJECTTYPE_DATA_LABELS:
                aDispatchCommand = ".uno:FormatDataLabels";
                break;
            case OBJECTTYPE_DATA_SERIES:
                aDispatchCommand = ".uno:FormatDataSeries";
                break;
            case OBJECTTYPE_DATA_LABEL:
                aDispatchCommand = ".uno:FormatDataLabel";
                break;
            case OBJECTTYPE_DATA_POINT:
                aDispatchCommand = ".uno:FormatDataPoint";
                break;
            case OBJECTTYPE_DATA_AVERAGE_LINE:
                aDispatchCommand = ".uno:FormatMeanValue";
                break;
            case OBJECTTYPE_DATA_ERRORS_X:
                aDispatchCommand = ".uno:FormatXErrorBars";
                break;
            case OBJECTTYPE_DATA_ERRORS_Y:
                aDispatchCommand = ".uno:FormatYErrorBars";
                break;
            case OBJECTTYPE_DATA_ERRORS_Z:
                aDispatchCommand = ".uno:FormatZErrorBars";
                break;
            case OBJECTTYPE_DATA_CURVE:
                aDispatchCommand = ".uno:FormatTrendline";
                break;
            case OBJECTTYPE_DATA_CURVE_EQUATION:
                aDispatchCommand = ".uno:FormatTrendlineEquation";
                break;
            case OBJECTTYPE_DATA_STOCK_RANGE:
                aDispatchCommand = ".uno:FormatSelection";
                break;
            case OBJECTTYPE_DATA_STOCK_LOSS:
                aDispatchCommand = ".uno:FormatStockLoss";
                break;
            case OBJECTTYPE_DATA_STOCK_GAIN:
                aDispatchCommand = ".uno:FormatStockGain";
                break;
            default: //OBJECTTYPE_UNKNOWN
                break;
        }
        return aDispatchCommand;
    }

    } // anonymous namespace

    // awt::XWindow
    void SAL_CALL ChartController::setPosSize(
        sal_Int32 X,
        sal_Int32 Y,
        sal_Int32 Width,
        sal_Int32 Height,
        sal_Int16 Flags )
    {
        SolarMutexGuard aGuard;
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;
        auto pChartWindow(GetChartWindow());

        if(xWindow.is() && pChartWindow)
        {
            Size aLogicSize = pChartWindow->PixelToLogic( Size( Width, Height ), MapMode( MapUnit::Map100thMM )  );

            //todo: for standalone chart: detect whether we are standalone
            //change map mode to fit new size
            awt::Size aModelPageSize = ChartModelHelper::getPageSize( getModel() );
            sal_Int32 nScaleXNumerator = aLogicSize.Width();
            sal_Int32 nScaleXDenominator = aModelPageSize.Width;
            sal_Int32 nScaleYNumerator = aLogicSize.Height();
            sal_Int32 nScaleYDenominator = aModelPageSize.Height;
            MapMode aNewMapMode(
                        MapUnit::Map100thMM,
                        Point(0,0),
                        Fraction(nScaleXNumerator, nScaleXDenominator),
                        Fraction(nScaleYNumerator, nScaleYDenominator) );
            pChartWindow->SetMapMode(aNewMapMode);
            pChartWindow->setPosSizePixel( X, Y, Width, Height, static_cast<PosSizeFlags>(Flags) );

            //#i75867# poor quality of ole's alternative view with 3D scenes and zoomfactors besides 100%
            uno::Reference< beans::XPropertySet > xProp( m_xChartView, uno::UNO_QUERY );
            if( xProp.is() )
            {
                auto aZoomFactors(::comphelper::InitPropertySequence({
                    { "ScaleXNumerator", uno::Any( nScaleXNumerator ) },
                    { "ScaleXDenominator", uno::Any( nScaleXDenominator ) },
                    { "ScaleYNumerator", uno::Any( nScaleYNumerator ) },
                    { "ScaleYDenominator", uno::Any( nScaleYDenominator ) }
                }));
                xProp->setPropertyValue( "ZoomFactors", uno::Any( aZoomFactors ));
            }

            //a correct work area is at least necessary for correct values in the position and  size dialog and for dragging area
            if(m_pDrawViewWrapper)
            {
                tools::Rectangle aRect(Point(0,0), pChartWindow->GetOutputSize());
                m_pDrawViewWrapper->SetWorkArea( aRect );
            }
            pChartWindow->Invalidate();
        }
    }

    awt::Rectangle SAL_CALL ChartController::getPosSize()
    {
        //@todo
        awt::Rectangle aRet(0, 0, 0, 0);

        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;
        if(xWindow.is())
            aRet = xWindow->getPosSize();

        return aRet;
    }

    void SAL_CALL ChartController::setVisible( sal_Bool Visible )
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->setVisible( Visible );
    }

    void SAL_CALL ChartController::setEnable( sal_Bool Enable )
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->setEnable( Enable );
    }

    void SAL_CALL ChartController::setFocus()
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->setFocus();
    }

    void SAL_CALL ChartController::addWindowListener(
        const uno::Reference< awt::XWindowListener >& xListener )
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->addWindowListener( xListener );
    }

    void SAL_CALL ChartController::removeWindowListener(
        const uno::Reference< awt::XWindowListener >& xListener )
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->removeWindowListener( xListener );
    }

    void SAL_CALL ChartController::addFocusListener(
        const uno::Reference< awt::XFocusListener >& xListener )
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->addFocusListener( xListener );
    }

    void SAL_CALL ChartController::removeFocusListener(
        const uno::Reference< awt::XFocusListener >& xListener )
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->removeFocusListener( xListener );
    }

    void SAL_CALL ChartController::addKeyListener(
        const uno::Reference< awt::XKeyListener >& xListener )
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->addKeyListener( xListener );
    }

    void SAL_CALL ChartController::removeKeyListener(
        const uno::Reference< awt::XKeyListener >& xListener )
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->removeKeyListener( xListener );
    }

    void SAL_CALL ChartController::addMouseListener(
        const uno::Reference< awt::XMouseListener >& xListener )
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->addMouseListener( xListener );
    }

    void SAL_CALL ChartController::removeMouseListener(
        const uno::Reference< awt::XMouseListener >& xListener )
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->removeMouseListener( xListener );
    }

    void SAL_CALL ChartController::addMouseMotionListener(
        const uno::Reference< awt::XMouseMotionListener >& xListener )
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->addMouseMotionListener( xListener );
    }

    void SAL_CALL ChartController::removeMouseMotionListener(
        const uno::Reference< awt::XMouseMotionListener >& xListener )
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->removeMouseMotionListener( xListener );
    }

    void SAL_CALL ChartController::addPaintListener(
        const uno::Reference< awt::XPaintListener >& xListener )
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->addPaintListener( xListener );
    }

    void SAL_CALL ChartController::removePaintListener(
        const uno::Reference< awt::XPaintListener >& xListener )
    {
        //@todo
        uno::Reference<awt::XWindow> xWindow = m_xViewWindow;

        if(xWindow.is())
            xWindow->removePaintListener( xListener );
    }

    // impl vcl window controller methods
    void ChartController::PrePaint()
    {
        // forward VCLs PrePaint window event to DrawingLayer
        DrawViewWrapper* pDrawViewWrapper = m_pDrawViewWrapper.get();

        if (pDrawViewWrapper)
        {
            pDrawViewWrapper->PrePaint();
        }
    }

    void ChartController::execute_Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
    {
        try
        {
            uno::Reference<frame::XModel> xModel(getModel());
            //OSL_ENSURE( xModel.is(), "ChartController::execute_Paint: have no model to paint");
            if (!xModel.is())
                return;

            //better performance for big data
            uno::Reference<beans::XPropertySet> xProp(m_xChartView, uno::UNO_QUERY);
            if (xProp.is())
            {
                awt::Size aResolution(1000, 1000);
                {
                    SolarMutexGuard aGuard;
                    auto pChartWindow(GetChartWindow());
                    if (pChartWindow)
                    {
                        aResolution.Width = pChartWindow->GetSizePixel().Width();
                        aResolution.Height = pChartWindow->GetSizePixel().Height();
                    }
                }
                xProp->setPropertyValue( "Resolution", uno::Any( aResolution ));
            }

            uno::Reference< util::XUpdatable > xUpdatable( m_xChartView, uno::UNO_QUERY );
            if (xUpdatable.is())
                xUpdatable->update();

            {
                SolarMutexGuard aGuard;
                DrawViewWrapper* pDrawViewWrapper = m_pDrawViewWrapper.get();
                if (pDrawViewWrapper)
                    pDrawViewWrapper->CompleteRedraw(&rRenderContext, vcl::Region(rRect));
            }
        }
        catch( const uno::Exception & )
        {
            DBG_UNHANDLED_EXCEPTION("chart2");
        }
        catch( ... )
        {
        }
    }

    bool isDoubleClick( const MouseEvent& rMEvt )
    {
        return rMEvt.GetClicks() == 2 && rMEvt.IsLeft() &&
            !rMEvt.IsMod1() && !rMEvt.IsMod2() && !rMEvt.IsShift();
    }

    void ChartController::startDoubleClickWaiting()
    {
        SolarMutexGuard aGuard;

        m_bWaitingForDoubleClick = true;

        sal_uInt64 nDblClkTime = 500;
        auto pChartWindow(GetChartWindow());
        if( pChartWindow )
        {
            const MouseSettings& rMSettings = pChartWindow->GetSettings().GetMouseSettings();
            nDblClkTime = rMSettings.GetDoubleClickTime();
        }
        m_aDoubleClickTimer.SetTimeout( nDblClkTime );
        m_aDoubleClickTimer.Start();
    }

    void ChartController::stopDoubleClickWaiting()
    {
        m_aDoubleClickTimer.Stop();
        m_bWaitingForDoubleClick = false;
    }

    IMPL_LINK_NOARG(ChartController, DoubleClickWaitingHdl, Timer *, void)
    {
        m_bWaitingForDoubleClick = false;

        if( !m_bWaitingForMouseUp && m_aSelection.maybeSwitchSelectionAfterSingleClickWasEnsured() )
        {
            impl_selectObjectAndNotiy();
            SolarMutexGuard aGuard;
            auto pChartWindow(GetChartWindow());
            if( pChartWindow )
            {
                vcl::Window::PointerState aPointerState( pChartWindow->GetPointerState() );
                MouseEvent aMouseEvent(
                                aPointerState.maPos,
                                1/*nClicks*/,
                                MouseEventModifiers::NONE,
                                static_cast< sal_uInt16 >( aPointerState.mnState )/*nButtons*/,
                                0/*nModifier*/ );
                impl_SetMousePointer( aMouseEvent );
            }
        }
    }

    void ChartController::execute_MouseButtonDown( const MouseEvent& rMEvt )
    {
        SolarMutexGuard aGuard;

        m_bWaitingForMouseUp = true;
        m_bFieldButtonDown = false;

        if( isDoubleClick(rMEvt) )
            stopDoubleClickWaiting();
        else
            startDoubleClickWaiting();

        m_aSelection.remindSelectionBeforeMouseDown();

        DrawViewWrapper* pDrawViewWrapper = m_pDrawViewWrapper.get();
        auto pChartWindow(GetChartWindow());
        if(!pChartWindow || !pDrawViewWrapper )
            return;

        Point aMPos = pChartWindow->PixelToLogic(rMEvt.GetPosPixel());

        // Check if button was clicked
        SdrObject* pObject = pDrawViewWrapper->getHitObject(aMPos);
        if (pObject)
        {
            OUString aCID = pObject->GetName();
            if (aCID.startsWith("FieldButton"))
            {
                m_bFieldButtonDown = true;
                return; // Don't take any action if button was clicked
            }
        }

        if ( rMEvt.GetButtons() == MOUSE_LEFT )
        {
            pChartWindow->GrabFocus();
            pChartWindow->CaptureMouse();
        }

        if( pDrawViewWrapper->IsTextEdit() )
        {
            SdrViewEvent aVEvt;
            if ( pDrawViewWrapper->IsTextEditHit( aMPos ) ||
                 // #i12587# support for shapes in chart
                 ( rMEvt.IsRight() && pDrawViewWrapper->PickAnything( rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt ) == SdrHitKind::MarkedObject ) )
            {
                pDrawViewWrapper->MouseButtonDown(rMEvt, pChartWindow);
                return;
            }
            else
            {
                EndTextEdit();
            }
        }

        //abort running action
        if( pDrawViewWrapper->IsAction() )
        {
            if( rMEvt.IsRight() )
                pDrawViewWrapper->BckAction();
            return;
        }

        if( isDoubleClick(rMEvt) ) //do not change selection if double click
            return;//double click is handled further in mousebutton up

        SdrHdl* pHitSelectionHdl = nullptr;
        //switch from move to resize if handle is hit on a resizable object
        if( m_aSelection.isResizeableObjectSelected() )
            pHitSelectionHdl = pDrawViewWrapper->PickHandle( aMPos );
        //only change selection if no selection handles are hit
        if( !pHitSelectionHdl )
        {
            // #i12587# support for shapes in chart
            if ( m_eDrawMode == CHARTDRAW_INSERT &&
                 ( !pDrawViewWrapper->IsMarkedHit( aMPos ) || !m_aSelection.isDragableObjectSelected() ) )
            {
                if ( m_aSelection.hasSelection() )
                {
                    m_aSelection.clearSelection();
                }
                if ( !pDrawViewWrapper->IsAction() )
                {
                    if ( pDrawViewWrapper->GetCurrentObjIdentifier() == OBJ_CAPTION )
                    {
                        Size aCaptionSize( 2268, 1134 );
                        pDrawViewWrapper->BegCreateCaptionObj( aMPos, aCaptionSize );
                    }
                    else
                    {
                        pDrawViewWrapper->BegCreateObj( aMPos);
                    }
                    SdrObject* pObj = pDrawViewWrapper->GetCreateObj();
                    DrawCommandDispatch* pDrawCommandDispatch = m_aDispatchContainer.getDrawCommandDispatch();
                    if ( pObj && m_pDrawModelWrapper && pDrawCommandDispatch )
                    {
                        SfxItemSet aSet( m_pDrawModelWrapper->GetItemPool() );
                        pDrawCommandDispatch->setAttributes( pObj );
                        pDrawCommandDispatch->setLineEnds( aSet );
                        pObj->SetMergedItemSet( aSet );
                    }
                }
                impl_SetMousePointer( rMEvt );
                return;
            }

            m_aSelection.adaptSelectionToNewPos(
                            aMPos,
                            pDrawViewWrapper,
                            rMEvt.IsRight(),
                            m_bWaitingForDoubleClick );

            if( !m_aSelection.isRotateableObjectSelected( getModel() ) )
            {
                    m_eDragMode = SdrDragMode::Move;
                    pDrawViewWrapper->SetDragMode(m_eDragMode);
            }

            m_aSelection.applySelection(pDrawViewWrapper);
        }
        if( m_aSelection.isDragableObjectSelected()
            && !rMEvt.IsRight() )
        {
            //start drag
            sal_uInt16  nDrgLog = static_cast<sal_uInt16>(pChartWindow->PixelToLogic(Size(DRGPIX,0)).Width());
            SdrDragMethod* pDragMethod = nullptr;

            //change selection to 3D scene if rotate mode
            SdrDragMode eDragMode = pDrawViewWrapper->GetDragMode();
            if( eDragMode==SdrDragMode::Rotate )
            {
                E3dScene* pScene = SelectionHelper::getSceneToRotate( pDrawViewWrapper->getNamedSdrObject( m_aSelection.getSelectedCID() ) );
                if( pScene )
                {
                    DragMethod_RotateDiagram::RotationDirection eRotationDirection(DragMethod_RotateDiagram::ROTATIONDIRECTION_FREE);
                    if(pHitSelectionHdl)
                    {
                        SdrHdlKind eKind = pHitSelectionHdl->GetKind();
                        if( eKind==SdrHdlKind::Upper || eKind==SdrHdlKind::Lower )
                            eRotationDirection = DragMethod_RotateDiagram::ROTATIONDIRECTION_X;
                        else if( eKind==SdrHdlKind::Left || eKind==SdrHdlKind::Right )
                            eRotationDirection = DragMethod_RotateDiagram::ROTATIONDIRECTION_Y;
                        else if( eKind==SdrHdlKind::UpperLeft || eKind==SdrHdlKind::UpperRight || eKind==SdrHdlKind::LowerLeft || eKind==SdrHdlKind::LowerRight )
                            eRotationDirection = DragMethod_RotateDiagram::ROTATIONDIRECTION_Z;
                    }
                    pDragMethod = new DragMethod_RotateDiagram( *pDrawViewWrapper, m_aSelection.getSelectedCID(), getModel(), eRotationDirection );
                }
            }
            else
            {
                OUString aDragMethodServiceName( ObjectIdentifier::getDragMethodServiceName( m_aSelection.getSelectedCID() ) );
                if( aDragMethodServiceName == ObjectIdentifier::getPieSegmentDragMethodServiceName() )
                    pDragMethod = new DragMethod_PieSegment( *pDrawViewWrapper, m_aSelection.getSelectedCID(), getModel() );
            }
            pDrawViewWrapper->SdrView::BegDragObj(aMPos, nullptr, pHitSelectionHdl, nDrgLog, pDragMethod);
        }

        impl_SetMousePointer( rMEvt );
    }

    void ChartController::execute_MouseMove( const MouseEvent& rMEvt )
    {
        SolarMutexGuard aGuard;

        DrawViewWrapper* pDrawViewWrapper = m_pDrawViewWrapper.get();
        auto pChartWindow(GetChartWindow());
        if(!pChartWindow || !pDrawViewWrapper)
            return;

        if( m_pDrawViewWrapper->IsTextEdit() )
        {
            if( m_pDrawViewWrapper->MouseMove(rMEvt,pChartWindow) )
                return;
        }

        if(pDrawViewWrapper->IsAction())
        {
            pDrawViewWrapper->MovAction( pChartWindow->PixelToLogic( rMEvt.GetPosPixel() ) );
        }

        impl_SetMousePointer( rMEvt );
    }

    void ChartController::execute_MouseButtonUp( const MouseEvent& rMEvt )
    {
        ControllerLockGuardUNO aCLGuard( getModel() );
        bool bMouseUpWithoutMouseDown = !m_bWaitingForMouseUp;
        m_bWaitingForMouseUp = false;
        bool bNotifySelectionChange = false;
        {
            SolarMutexGuard aGuard;

            DrawViewWrapper* pDrawViewWrapper = m_pDrawViewWrapper.get();
            auto pChartWindow(GetChartWindow());
            if(!pChartWindow || !pDrawViewWrapper)
                return;

            Point aMPos = pChartWindow->PixelToLogic(rMEvt.GetPosPixel());

            // Check if button was clicked
            if (m_bFieldButtonDown)
            {
                m_bFieldButtonDown = false;
                SdrObject* pObject = pDrawViewWrapper->getHitObject(aMPos);
                if (pObject)
                {
                    OUString aCID = pObject->GetName();
                    if (aCID.startsWith("FieldButton"))
                    {
                        sendPopupRequest(aCID, pObject->GetCurrentBoundRect());
                        return;
                    }
                }
            }

            if(pDrawViewWrapper->IsTextEdit())
            {
                if( pDrawViewWrapper->MouseButtonUp(rMEvt,pChartWindow) )
                    return;
            }

            // #i12587# support for shapes in chart
            if ( m_eDrawMode == CHARTDRAW_INSERT && pDrawViewWrapper->IsCreateObj() )
            {
                pDrawViewWrapper->EndCreateObj( SdrCreateCmd::ForceEnd );
                {
                    HiddenUndoContext aUndoContext( m_xUndoManager );
                        // don't want the positioning Undo action to appear in the UI
                    impl_switchDiagramPositioningToExcludingPositioning();
                }
                if ( pDrawViewWrapper->AreObjectsMarked() )
                {
                    if ( pDrawViewWrapper->GetCurrentObjIdentifier() == OBJ_TEXT )
                    {
                        executeDispatch_EditText();
                    }
                    else
                    {
                        SdrObject* pObj = pDrawViewWrapper->getSelectedObject();
                        if ( pObj )
                        {
                            uno::Reference< drawing::XShape > xShape( pObj->getUnoShape(), uno::UNO_QUERY );
                            if ( xShape.is() )
                            {
                                m_aSelection.setSelection( xShape );
                                m_aSelection.applySelection( pDrawViewWrapper );
                            }
                        }
                    }
                }
                else
                {
                    m_aSelection.adaptSelectionToNewPos( aMPos, pDrawViewWrapper, rMEvt.IsRight(), m_bWaitingForDoubleClick );
                    m_aSelection.applySelection( pDrawViewWrapper );
                    setDrawMode( CHARTDRAW_SELECT );
                }
            }
            else if ( pDrawViewWrapper->IsDragObj() )
            {
                bool bDraggingDone = false;
                SdrDragMethod* pDragMethod = pDrawViewWrapper->SdrView::GetDragMethod();
                bool bIsMoveOnly = pDragMethod && pDragMethod->getMoveOnly();
                DragMethod_Base* pChartDragMethod = dynamic_cast< DragMethod_Base* >(pDragMethod);
                if( pChartDragMethod )
                {
                    UndoGuard aUndoGuard( pChartDragMethod->getUndoDescription(),
                            m_xUndoManager );

                    if( pDrawViewWrapper->EndDragObj() )
                    {
                        bDraggingDone = true;
                        aUndoGuard.commit();
                    }
                }

                if( !bDraggingDone && pDrawViewWrapper->EndDragObj() )
                {
                    try
                    {
                        //end move or size
                        SdrObject* pObj = pDrawViewWrapper->getSelectedObject();
                        if( pObj )
                        {
                            tools::Rectangle aObjectRect = pObj->GetSnapRect();
                            awt::Size aPageSize( ChartModelHelper::getPageSize( getModel() ) );
                            tools::Rectangle aPageRect( 0,0,aPageSize.Width,aPageSize.Height );

                            const E3dObject* pE3dObject(dynamic_cast< const E3dObject*>(pObj));
                            if(nullptr != pE3dObject)
                            {
                                E3dScene* pScene(pE3dObject->getRootE3dSceneFromE3dObject());
                                if(nullptr != pScene)
                                {
                                    aObjectRect = pScene->GetSnapRect();
                                }
                            }

                            ActionDescriptionProvider::ActionType eActionType(ActionDescriptionProvider::ActionType::Move);
                            if( !bIsMoveOnly && m_aSelection.isResizeableObjectSelected() )
                                eActionType = ActionDescriptionProvider::ActionType::Resize;

                            ObjectType eObjectType = ObjectIdentifier::getObjectType( m_aSelection.getSelectedCID() );

                            UndoGuard aUndoGuard(
                                ActionDescriptionProvider::createDescription( eActionType, ObjectNameProvider::getName( eObjectType)),
                                m_xUndoManager );

                            bool bChanged = false;
                            ChartModel* pModel = dynamic_cast<ChartModel*>(getModel().get());
                            if ( eObjectType == OBJECTTYPE_LEGEND )
                                bChanged = DiagramHelper::switchDiagramPositioningToExcludingPositioning( *pModel, false , true );

                            bool bMoved = PositionAndSizeHelper::moveObject( m_aSelection.getSelectedCID()
                                            , getModel()
                                            , awt::Rectangle(aObjectRect.getX(),aObjectRect.getY(),aObjectRect.getWidth(),aObjectRect.getHeight())
                                            , awt::Rectangle(aPageRect.getX(),aPageRect.getY(),aPageRect.getWidth(),aPageRect.getHeight()) );

                            if( bMoved || bChanged )
                            {
                                bDraggingDone = true;
                                aUndoGuard.commit();
                            }
                        }
                    }
                    catch( const uno::Exception & )
                    {
                        DBG_UNHANDLED_EXCEPTION("chart2");
                    }
                    //all wanted model changes will take effect
                    //and all unwanted view modifications are cleaned
                }

                if( !bDraggingDone ) //mouse wasn't moved while dragging
                {
                    bool bClickedTwiceOnDragableObject = SelectionHelper::isDragableObjectHitTwice( aMPos, m_aSelection.getSelectedCID(), *pDrawViewWrapper );
                    bool bIsRotateable = m_aSelection.isRotateableObjectSelected( getModel() );

                    //toggle between move and rotate
                    if( bIsRotateable && bClickedTwiceOnDragableObject && m_eDragMode==SdrDragMode::Move )
                        m_eDragMode=SdrDragMode::Rotate;
                    else
                        m_eDragMode=SdrDragMode::Move;

                    pDrawViewWrapper->SetDragMode(m_eDragMode);

                    if( !m_bWaitingForDoubleClick && m_aSelection.maybeSwitchSelectionAfterSingleClickWasEnsured() )
                    {
                        impl_selectObjectAndNotiy();
                    }
                }
                else
                    m_aSelection.resetPossibleSelectionAfterSingleClickWasEnsured();
            }
            // In tiled rendering drag mode could be not yet over on the call
            // that should handle the double-click, so better to perform this check
            // always.
            if( isDoubleClick(rMEvt) && !bMouseUpWithoutMouseDown /*#i106966#*/ )
            {
                Point aMousePixel = rMEvt.GetPosPixel();
                execute_DoubleClick( &aMousePixel );
            }

            //@todo ForcePointer(&rMEvt);
            pChartWindow->ReleaseMouse();

            if( m_aSelection.isSelectionDifferentFromBeforeMouseDown() )
                bNotifySelectionChange = true;
        }

        impl_SetMousePointer( rMEvt );

        if(bNotifySelectionChange)
            impl_notifySelectionChangeListeners();
    }

    void ChartController::execute_DoubleClick( const Point* pMousePixel )
    {
        bool bEditText = false;
        if ( m_aSelection.hasSelection() )
        {
            OUString aCID( m_aSelection.getSelectedCID() );
            if ( !aCID.isEmpty() )
            {
                ObjectType eObjectType = ObjectIdentifier::getObjectType( aCID );
                if ( eObjectType == OBJECTTYPE_TITLE )
                {
                    bEditText = true;
                }
            }
            else
            {
                // #i12587# support for shapes in chart
                SdrObject* pObj = DrawViewWrapper::getSdrObject( m_aSelection.getSelectedAdditionalShape() );
                if ( dynamic_cast< const SdrTextObj* >(pObj) !=  nullptr )
                {
                    bEditText = true;
                }
            }
        }

        if ( bEditText )
        {
            executeDispatch_EditText( pMousePixel );
        }
        else
        {
            executeDispatch_ObjectProperties();
        }
    }

    void ChartController::execute_Resize()
    {
        SolarMutexGuard aGuard;
        auto pChartWindow(GetChartWindow());
        if(pChartWindow)
            pChartWindow->Invalidate();
    }

    void ChartController::execute_Command( const CommandEvent& rCEvt )
    {
        SolarMutexGuard aGuard;
        auto pChartWindow(GetChartWindow());
        bool bIsAction = false;
        {
            DrawViewWrapper* pDrawViewWrapper = m_pDrawViewWrapper.get();
            if(!pChartWindow || !pDrawViewWrapper)
                return;
            bIsAction = m_pDrawViewWrapper->IsAction();
        }

        // pop-up menu
        if(rCEvt.GetCommand() == CommandEventId::ContextMenu && !bIsAction)
        {
            {
                if(pChartWindow)
                    pChartWindow->ReleaseMouse();
            }

            if( m_aSelection.isSelectionDifferentFromBeforeMouseDown() )
                impl_notifySelectionChangeListeners();

            css::uno::Reference< css::awt::XPopupMenu > xPopupMenu( m_xCC->getServiceManager()->createInstanceWithContext(
                "com.sun.star.awt.PopupMenu", m_xCC ), css::uno::UNO_QUERY );

            Point aPos( rCEvt.GetMousePosPixel() );
            if( !rCEvt.IsMouseEvent() )
            {
                if(pChartWindow)
                    aPos = pChartWindow->GetPointerState().maPos;
            }

            OUString aMenuName;
            if ( isShapeContext() )
                // #i12587# support for shapes in chart
                aMenuName = m_pDrawViewWrapper->IsTextEdit() ? OUString( "drawtext" ) : OUString( "draw" );
            else
            {
                // todo: the context menu should be specified by an xml file in uiconfig
                if( xPopupMenu.is())
                {
                    sal_Int16 nUniqueId = 1;
                    lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:Cut" );
                    lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:Copy" );
                    lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:Paste" );
                    xPopupMenu->insertSeparator( -1 );

                    ObjectType eObjectType = ObjectIdentifier::getObjectType( m_aSelection.getSelectedCID() );
                    Reference< XDiagram > xDiagram = ChartModelHelper::findDiagram( getModel() );

                    OUString aFormatCommand( lcl_getFormatCommandForObjectCID( m_aSelection.getSelectedCID() ) );
                    lcl_insertMenuCommand( xPopupMenu, nUniqueId++, aFormatCommand );

                    //some commands for dataseries and points:

                    if( eObjectType == OBJECTTYPE_DATA_SERIES || eObjectType == OBJECTTYPE_DATA_POINT )
                    {
                        bool bIsPoint = ( eObjectType == OBJECTTYPE_DATA_POINT );
                        uno::Reference< XDataSeries > xSeries = ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getModel() );
                        uno::Reference< chart2::XRegressionCurveContainer > xCurveCnt( xSeries, uno::UNO_QUERY );
                        Reference< chart2::XRegressionCurve > xTrendline( RegressionCurveHelper::getFirstCurveNotMeanValueLine( xCurveCnt ) );
                        bool bHasEquation = RegressionCurveHelper::hasEquation( xTrendline );
                        Reference< chart2::XRegressionCurve > xMeanValue( RegressionCurveHelper::getMeanValueLine( xCurveCnt ) );
                        bool bHasYErrorBars = StatisticsHelper::hasErrorBars( xSeries );
                        bool bHasXErrorBars = StatisticsHelper::hasErrorBars( xSeries, false );
                        bool bHasDataLabelsAtSeries = DataSeriesHelper::hasDataLabelsAtSeries( xSeries );
                        bool bHasDataLabelsAtPoints = DataSeriesHelper::hasDataLabelsAtPoints( xSeries );
                        bool bHasDataLabelAtPoint = false;
                        sal_Int32 nPointIndex = -1;
                        if( bIsPoint )
                        {
                            nPointIndex = ObjectIdentifier::getIndexFromParticleOrCID( m_aSelection.getSelectedCID() );
                            bHasDataLabelAtPoint = DataSeriesHelper::hasDataLabelAtPoint( xSeries, nPointIndex );
                        }
                        bool bSelectedPointIsFormatted = false;
                        bool bHasFormattedDataPointsOtherThanSelected = false;

                        Reference< beans::XPropertySet > xSeriesProperties( xSeries, uno::UNO_QUERY );
                        if( xSeriesProperties.is() )
                        {
                            uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
                            if( xSeriesProperties->getPropertyValue( "AttributedDataPoints" ) >>= aAttributedDataPointIndexList )
                            {
                                if( aAttributedDataPointIndexList.hasElements() )
                                {
                                    if( bIsPoint )
                                    {
                                        std::vector< sal_Int32 > aIndices( ContainerHelper::SequenceToVector( aAttributedDataPointIndexList ) );
                                        std::vector< sal_Int32 >::iterator aIt = std::find( aIndices.begin(), aIndices.end(), nPointIndex );
                                        if( aIt != aIndices.end())
                                            bSelectedPointIsFormatted = true;
                                        else
                                            bHasFormattedDataPointsOtherThanSelected = true;
                                    }
                                    else
                                        bHasFormattedDataPointsOtherThanSelected = true;
                                }
                            }
                        }

                        if( bIsPoint )
                        {
                            if( bHasDataLabelAtPoint )
                                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:FormatDataLabel" );
                            if( !bHasDataLabelAtPoint )
                                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:InsertDataLabel" );
                            else
                                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:DeleteDataLabel" );
                            if( bSelectedPointIsFormatted )
                                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:ResetDataPoint" );

                            xPopupMenu->insertSeparator( -1 );

                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:FormatDataSeries" );
                        }

                        Reference< chart2::XChartType > xChartType( DiagramHelper::getChartTypeOfSeries( xDiagram, xSeries ) );
                        if( xChartType->getChartType() == CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK )
                        {
                            try
                            {
                                Reference< beans::XPropertySet > xChartTypeProp( xChartType, uno::UNO_QUERY );
                                if( xChartTypeProp.is() )
                                {
                                    bool bJapaneseStyle = false;
                                    xChartTypeProp->getPropertyValue( "Japanese" ) >>= bJapaneseStyle;

                                    if( bJapaneseStyle )
                                    {
                                        lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:FormatStockLoss" );
                                        lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:FormatStockGain" );
                                    }
                                }
                            }
                            catch( const uno::Exception & )
                            {
                                DBG_UNHANDLED_EXCEPTION("chart2");
                            }
                        }

                        if( bHasDataLabelsAtSeries )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:FormatDataLabels" );
                        if( bHasEquation )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:FormatTrendlineEquation" );
                        if( xMeanValue.is() )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:FormatMeanValue" );
                        if( bHasXErrorBars )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:FormatXErrorBars" );
                        if( bHasYErrorBars )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:FormatYErrorBars" );

                        xPopupMenu->insertSeparator( -1 );

                        if( !bHasDataLabelsAtSeries )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:InsertDataLabels" );

                        lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:InsertTrendline" );

                        if( !xMeanValue.is() )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:InsertMeanValue" );
                        if( !bHasXErrorBars )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:InsertXErrorBars" );
                        if( !bHasYErrorBars )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:InsertYErrorBars" );
                        if( bHasDataLabelsAtSeries || ( bHasDataLabelsAtPoints && bHasFormattedDataPointsOtherThanSelected ) )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:DeleteDataLabels" );
                        if( bHasEquation )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:DeleteTrendlineEquation" );
                        if( xMeanValue.is() )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:DeleteMeanValue" );
                        if( bHasXErrorBars )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:DeleteXErrorBars" );
                        if( bHasYErrorBars )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:DeleteYErrorBars" );

                        if( bHasFormattedDataPointsOtherThanSelected )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:ResetAllDataPoints" );

                        xPopupMenu->insertSeparator( -1 );

                        lcl_insertMenuCommand( xPopupMenu, nUniqueId, ".uno:ArrangeRow" );
                        uno::Reference< awt::XPopupMenu > xArrangePopupMenu(
                            m_xCC->getServiceManager()->createInstanceWithContext(
                                "com.sun.star.awt.PopupMenu", m_xCC ), uno::UNO_QUERY );
                        if( xArrangePopupMenu.is() )
                        {
                            sal_Int16 nSubId = nUniqueId + 1;
                            lcl_insertMenuCommand( xArrangePopupMenu, nSubId++, ".uno:Forward" );
                            lcl_insertMenuCommand( xArrangePopupMenu, nSubId, ".uno:Backward" );
                            xPopupMenu->setPopupMenu( nUniqueId, xArrangePopupMenu );
                            nUniqueId = nSubId;
                        }
                        ++nUniqueId;
                    }
                    else if( eObjectType == OBJECTTYPE_DATA_CURVE )
                    {
                        lcl_insertMenuCommand( xPopupMenu,  nUniqueId++, ".uno:DeleteTrendline" );
                        lcl_insertMenuCommand( xPopupMenu,  nUniqueId++, ".uno:FormatTrendlineEquation" );
                        lcl_insertMenuCommand( xPopupMenu,  nUniqueId++, ".uno:InsertTrendlineEquation" );
                        lcl_insertMenuCommand( xPopupMenu,  nUniqueId++, ".uno:InsertTrendlineEquationAndR2" );
                        lcl_insertMenuCommand( xPopupMenu,  nUniqueId++, ".uno:InsertR2Value" );
                        lcl_insertMenuCommand( xPopupMenu,  nUniqueId++, ".uno:DeleteTrendlineEquation" );
                        lcl_insertMenuCommand( xPopupMenu,  nUniqueId++, ".uno:DeleteR2Value" );
                    }
                    else if( eObjectType == OBJECTTYPE_DATA_CURVE_EQUATION )
                    {
                        lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:InsertR2Value" );
                        lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:DeleteR2Value" );
                    }

                    //some commands for axes: and grids

                    else if( eObjectType  == OBJECTTYPE_AXIS || eObjectType == OBJECTTYPE_GRID || eObjectType == OBJECTTYPE_SUBGRID )
                    {
                        Reference< XAxis > xAxis = ObjectIdentifier::getAxisForCID( m_aSelection.getSelectedCID(), getModel() );
                        if( xAxis.is() && xDiagram.is() )
                        {
                            sal_Int32 nDimensionIndex = -1;
                            sal_Int32 nCooSysIndex = -1;
                            sal_Int32 nAxisIndex = -1;
                            AxisHelper::getIndicesForAxis( xAxis, xDiagram, nCooSysIndex, nDimensionIndex, nAxisIndex );
                            bool bIsSecondaryAxis = nAxisIndex!=0;
                            bool bIsAxisVisible = AxisHelper::isAxisVisible( xAxis );
                            bool bIsMajorGridVisible = AxisHelper::isGridShown( nDimensionIndex, nCooSysIndex, true /*bMainGrid*/, xDiagram );
                            bool bIsMinorGridVisible = AxisHelper::isGridShown( nDimensionIndex, nCooSysIndex, false /*bMainGrid*/, xDiagram );
                            bool bHasTitle = false;
                            uno::Reference< XTitled > xTitled( xAxis, uno::UNO_QUERY );
                            if( xTitled.is())
                                bHasTitle = !TitleHelper::getCompleteString( xTitled->getTitleObject() ).isEmpty();

                            if( eObjectType  != OBJECTTYPE_AXIS && bIsAxisVisible )
                                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:FormatAxis" );
                            if( eObjectType != OBJECTTYPE_GRID && bIsMajorGridVisible && !bIsSecondaryAxis )
                                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:FormatMajorGrid" );
                            if( eObjectType != OBJECTTYPE_SUBGRID && bIsMinorGridVisible && !bIsSecondaryAxis )
                                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:FormatMinorGrid" );

                            xPopupMenu->insertSeparator( -1 );

                            if( eObjectType  != OBJECTTYPE_AXIS && !bIsAxisVisible )
                                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:InsertAxis" );
                            if( eObjectType != OBJECTTYPE_GRID && !bIsMajorGridVisible && !bIsSecondaryAxis )
                                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:InsertMajorGrid" );
                            if( eObjectType != OBJECTTYPE_SUBGRID && !bIsMinorGridVisible && !bIsSecondaryAxis )
                                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:InsertMinorGrid" );
                            if( !bHasTitle )
                                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:InsertAxisTitle" );

                            if( bIsAxisVisible )
                                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:DeleteAxis" );
                            if( bIsMajorGridVisible && !bIsSecondaryAxis )
                                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:DeleteMajorGrid" );
                            if( bIsMinorGridVisible && !bIsSecondaryAxis )
                                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:DeleteMinorGrid" );
                        }
                    }

                    if( eObjectType == OBJECTTYPE_DATA_STOCK_LOSS )
                        lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:FormatStockGain" );
                    else if( eObjectType == OBJECTTYPE_DATA_STOCK_GAIN )
                        lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:FormatStockLoss" );

                    lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:TransformDialog" );

                    if( eObjectType == OBJECTTYPE_PAGE || eObjectType == OBJECTTYPE_DIAGRAM
                        || eObjectType == OBJECTTYPE_DIAGRAM_WALL
                        || eObjectType == OBJECTTYPE_DIAGRAM_FLOOR
                        || eObjectType == OBJECTTYPE_UNKNOWN )
                    {
                        if( eObjectType != OBJECTTYPE_UNKNOWN )
                            xPopupMenu->insertSeparator( -1 );
                        bool bHasLegend = LegendHelper::hasLegend( xDiagram );
                        lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:InsertTitles" );
                        if( !bHasLegend )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:InsertLegend" );
                        lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:InsertRemoveAxes" );
                        if( bHasLegend )
                            lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:DeleteLegend" );
                    }

                    xPopupMenu->insertSeparator( -1 );
                    lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:DiagramType" );
                    lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:DataRanges" );
                    lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:DiagramData" );
                    lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:View3D" );
                }
            }

            css::uno::Sequence< css::uno::Any > aArgs( 3 );
            aArgs[0] <<= comphelper::makePropertyValue( "IsContextMenu", true );
            aArgs[1] <<= comphelper::makePropertyValue( "Frame", m_xFrame );
            aArgs[2] <<= comphelper::makePropertyValue( "Value", aMenuName );

            css::uno::Reference< css::frame::XPopupMenuController > xPopupController(
                m_xCC->getServiceManager()->createInstanceWithArgumentsAndContext(
                "com.sun.star.comp.framework.ResourceMenuController", aArgs, m_xCC ), css::uno::UNO_QUERY );

            if ( !xPopupController.is() || !xPopupMenu.is() )
                return;

            if (comphelper::LibreOfficeKit::isActive())
            {
                PopupMenu* pPopupMenu = static_cast<PopupMenu*>(VCLXMenu::GetImplementation(xPopupMenu)->GetMenu());
                pPopupMenu->SetLOKNotifier(SfxViewShell::Current());

                // the context menu expects a position related to the document window,
                // not to the chart window
                SfxInPlaceClient* pIPClient = SfxViewShell::Current()->GetIPClient();
                if (pIPClient)
                {
                    vcl::Window* pRootWin = pIPClient->GetEditWin();
                    if (pRootWin)
                    {
                        Point aOffset = pChartWindow->GetOffsetPixelFrom(*pRootWin);
                        aPos += aOffset;
                    }
                }
            }

            xPopupController->setPopupMenu( xPopupMenu );
            xPopupMenu->execute( css::uno::Reference< css::awt::XWindowPeer >( m_xFrame->getContainerWindow(), css::uno::UNO_QUERY ),
                                 css::awt::Rectangle( aPos.X(), aPos.Y(), 0, 0 ),
                                 css::awt::PopupMenuDirection::EXECUTE_DEFAULT );

            css::uno::Reference< css::lang::XComponent > xComponent( xPopupController, css::uno::UNO_QUERY );
            if ( xComponent.is() )
                xComponent->dispose();
        }
        else if( ( rCEvt.GetCommand() == CommandEventId::StartExtTextInput ) ||
                 ( rCEvt.GetCommand() == CommandEventId::ExtTextInput ) ||
                 ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput ) ||
                 ( rCEvt.GetCommand() == CommandEventId::InputContextChange ) )
        {
            //#i84417# enable editing with IME
            if( m_pDrawViewWrapper )
                m_pDrawViewWrapper->Command( rCEvt, pChartWindow );
        }
    }

    bool ChartController::execute_KeyInput( const KeyEvent& rKEvt )
    {
        SolarMutexGuard aGuard;
        bool bReturn=false;

        DrawViewWrapper* pDrawViewWrapper = m_pDrawViewWrapper.get();
        auto pChartWindow(GetChartWindow());
        if(!pChartWindow || !pDrawViewWrapper)
            return bReturn;

        // handle accelerators
        if( ! m_apAccelExecute.get() && m_xFrame.is() && m_xCC.is() )
        {
            m_apAccelExecute = ::svt::AcceleratorExecute::createAcceleratorHelper();
            OSL_ASSERT( m_apAccelExecute.get());
            if( m_apAccelExecute.get() )
                m_apAccelExecute->init( m_xCC, m_xFrame );
        }

        vcl::KeyCode aKeyCode( rKEvt.GetKeyCode());
        sal_uInt16 nCode = aKeyCode.GetCode();
        bool bAlternate = aKeyCode.IsMod2();
        bool bCtrl = aKeyCode.IsMod1();

        if( m_apAccelExecute.get() )
            bReturn = m_apAccelExecute->execute( aKeyCode );
        if( bReturn )
            return bReturn;

        {
            if( pDrawViewWrapper->IsTextEdit() )
            {
                if( pDrawViewWrapper->KeyInput(rKEvt, pChartWindow) )
                {
                    bReturn = true;
                    if( nCode == KEY_ESCAPE )
                    {
                        EndTextEdit();
                    }
                }
            }
        }

        // keyboard accessibility
        ObjectType eObjectType = ObjectIdentifier::getObjectType( m_aSelection.getSelectedCID() );
        if( ! bReturn )
        {
            // Navigation (Tab/F3/Home/End)
            uno::Reference< XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
            ObjectKeyNavigation aObjNav( m_aSelection.getSelectedOID(), xChartDoc, ExplicitValueProvider::getExplicitValueProvider( m_xChartView ));
            awt::KeyEvent aKeyEvent( ::svt::AcceleratorExecute::st_VCLKey2AWTKey( aKeyCode ));
            bReturn = aObjNav.handleKeyEvent( aKeyEvent );
            if( bReturn )
            {
                ObjectIdentifier aNewOID = aObjNav.getCurrentSelection();
                uno::Any aNewSelection;
                if ( aNewOID.isValid() && !ObjectHierarchy::isRootNode( aNewOID ) )
                {
                    aNewSelection = aNewOID.getAny();
                }
                if ( m_eDragMode == SdrDragMode::Rotate && !SelectionHelper::isRotateableObject( aNewOID.getObjectCID(), getModel() ) )
                {
                    m_eDragMode = SdrDragMode::Move;
                }
                bReturn = select( aNewSelection );
            }
        }

        // Position and Size (+/-/arrow-keys) or pie segment dragging
        if( ! bReturn  )
        {
            // pie segment dragging
            // note: could also be done for data series
            if( eObjectType == OBJECTTYPE_DATA_POINT &&
                ObjectIdentifier::getDragMethodServiceName( m_aSelection.getSelectedCID() ) ==
                    ObjectIdentifier::getPieSegmentDragMethodServiceName())
            {
                bool bDrag = false;
                bool bDragInside = false;
                if( nCode == KEY_ADD ||
                    nCode == KEY_SUBTRACT )
                {
                    bDrag = true;
                    bDragInside = ( nCode == KEY_SUBTRACT );
                }
                else if(
                    nCode == KEY_LEFT ||
                    nCode == KEY_RIGHT ||
                    nCode == KEY_UP ||
                    nCode == KEY_DOWN )
                {
                    bDrag = true;
                    OUString aParameter( ObjectIdentifier::getDragParameterString( m_aSelection.getSelectedCID() ));
                    sal_Int32 nOffsetPercentDummy( 0 );
                    awt::Point aMinimumPosition( 0, 0 );
                    awt::Point aMaximumPosition( 0, 0 );
                    ObjectIdentifier::parsePieSegmentDragParameterString(
                        aParameter, nOffsetPercentDummy, aMinimumPosition, aMaximumPosition );
                    aMaximumPosition.Y -= aMinimumPosition.Y;
                    aMaximumPosition.X -= aMinimumPosition.X;

                    bDragInside =
                        (nCode == KEY_RIGHT && (aMaximumPosition.X < 0)) ||
                        (nCode == KEY_LEFT  && (aMaximumPosition.X > 0)) ||
                        (nCode == KEY_DOWN  && (aMaximumPosition.Y < 0)) ||
                        (nCode == KEY_UP    && (aMaximumPosition.Y > 0));
                }

                if( bDrag )
                {
                    double fAmount = bAlternate ? 0.01 : 0.05;
                    if( bDragInside )
                        fAmount *= -1.0;

                    bReturn = impl_DragDataPoint( m_aSelection.getSelectedCID(), fAmount );
                }
            }
            else
            {
                // size
                if( nCode == KEY_ADD ||
                    nCode == KEY_SUBTRACT )
                {
                    if( eObjectType == OBJECTTYPE_DIAGRAM )
                    {
                        // default 1 mm in each direction
                        double fGrowAmountX = 200.0;
                        double fGrowAmountY = 200.0;
                        if( bAlternate && pChartWindow )
                        {
                            // together with Alt-key: 1 px in each direction
                            if( pChartWindow )
                            {
                                Size aPixelSize = pChartWindow->PixelToLogic( Size( 2, 2 ));
                                fGrowAmountX = static_cast< double >( aPixelSize.Width());
                                fGrowAmountY = static_cast< double >( aPixelSize.Height());
                            }
                        }
                        if( nCode == KEY_SUBTRACT )
                        {
                            fGrowAmountX = -fGrowAmountX;
                            fGrowAmountY = -fGrowAmountY;
                        }
                        bReturn = impl_moveOrResizeObject(
                            m_aSelection.getSelectedCID(), CENTERED_RESIZE_OBJECT, fGrowAmountX, fGrowAmountY );
                    }
                }
                // position
                else if( nCode == KEY_LEFT  ||
                         nCode == KEY_RIGHT ||
                         nCode == KEY_UP ||
                         nCode == KEY_DOWN )
                {
                    if( m_aSelection.isDragableObjectSelected() )
                    {
                        // default 1 mm
                        double fShiftAmountX = 100.0;
                        double fShiftAmountY = 100.0;
                        if( bAlternate && pChartWindow )
                        {
                            // together with Alt-key: 1 px
                            if(pChartWindow)
                            {
                                Size aPixelSize = pChartWindow->PixelToLogic( Size( 1, 1 ));
                                fShiftAmountX = static_cast< double >( aPixelSize.Width());
                                fShiftAmountY = static_cast< double >( aPixelSize.Height());
                            }
                        }
                        switch( nCode )
                        {
                            case KEY_LEFT:
                                fShiftAmountX = -fShiftAmountX;
                                fShiftAmountY = 0.0;
                                break;
                            case KEY_RIGHT:
                                fShiftAmountY = 0.0;
                                break;
                            case KEY_UP:
                                fShiftAmountX = 0.0;
                                fShiftAmountY = -fShiftAmountY;
                                break;
                            case KEY_DOWN:
                                fShiftAmountX = 0.0;
                                break;
                        }
                        if( !m_aSelection.getSelectedCID().isEmpty() )
                        {
                            //move chart objects
                            bReturn = impl_moveOrResizeObject(
                                m_aSelection.getSelectedCID(), MOVE_OBJECT, fShiftAmountX, fShiftAmountY );
                        }
                        else
                        {
                            //move additional shapes
                            uno::Reference< drawing::XShape > xShape( m_aSelection.getSelectedAdditionalShape() );
                            if( xShape.is() )
                            {
                                awt::Point aPos( xShape->getPosition() );
                                awt::Size aSize( xShape->getSize() );
                                awt::Size aPageSize( ChartModelHelper::getPageSize( getModel() ) );
                                aPos.X = static_cast< long >( static_cast< double >( aPos.X ) + fShiftAmountX );
                                aPos.Y = static_cast< long >( static_cast< double >( aPos.Y ) + fShiftAmountY );
                                if( aPos.X + aSize.Width > aPageSize.Width )
                                    aPos.X = aPageSize.Width - aSize.Width;
                                if( aPos.X < 0 )
                                    aPos.X = 0;
                                if( aPos.Y + aSize.Height > aPageSize.Height )
                                    aPos.Y = aPageSize.Height - aSize.Height;
                                if( aPos.Y < 0 )
                                    aPos.Y = 0;

                                xShape->setPosition( aPos );
                            }
                        }
                    }
                }
            }
        }

        // dumping the shape
        if( !bReturn && bCtrl && nCode == KEY_F12)
        {
            uno::Reference< qa::XDumper > xChartModel( getModel(), uno::UNO_QUERY );
            if(xChartModel.is())
            {
                OUString aDump = xChartModel->dump();
                SAL_WARN("chart2", aDump);
            }
        }

        // text edit
        if( ! bReturn &&
            nCode == KEY_F2 )
        {
            if( eObjectType == OBJECTTYPE_TITLE )
            {
                executeDispatch_EditText();
                bReturn = true;
            }
        }

        // deactivate inplace mode (this code should be unnecessary, but
        // unfortunately is not)
        if( ! bReturn &&
            nCode == KEY_ESCAPE )
        {
            uno::Reference< frame::XDispatchHelper > xDispatchHelper( frame::DispatchHelper::create(m_xCC) );
            uno::Sequence< beans::PropertyValue > aArgs;
            xDispatchHelper->executeDispatch(
                uno::Reference< frame::XDispatchProvider >( m_xFrame, uno::UNO_QUERY ),
                ".uno:TerminateInplaceActivation",
                "_parent",
                frame::FrameSearchFlag::PARENT,
                aArgs );
            bReturn = true;
        }

        if( ! bReturn &&
            (nCode == KEY_DELETE || nCode == KEY_BACKSPACE ))
        {
            bReturn = executeDispatch_Delete();
            if( ! bReturn )
            {
                std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pChartWindow ? pChartWindow->GetFrameWeld() : nullptr,
                                                              VclMessageType::Info, VclButtonsType::Ok,
                                                              SchResId(STR_ACTION_NOTPOSSIBLE)));
                xInfoBox->run();
            }
        }

        return bReturn;
    }

    bool ChartController::requestQuickHelp(
        ::Point aAtLogicPosition,
        bool bIsBalloonHelp,
        OUString & rOutQuickHelpText,
        awt::Rectangle & rOutEqualRect )
    {
        uno::Reference< frame::XModel > xChartModel;
        if( m_aModel.is())
            xChartModel.set( getModel() );
        if( !xChartModel.is())
            return false;

        // help text
        OUString aCID;
        if( m_pDrawViewWrapper )
        {
            aCID = SelectionHelper::getHitObjectCID(
                aAtLogicPosition, *m_pDrawViewWrapper );
        }
        bool bResult( !aCID.isEmpty());

        if( bResult )
        {
            // get help text
            rOutQuickHelpText = ObjectNameProvider::getHelpText( aCID, xChartModel, bIsBalloonHelp /* bVerbose */ );

            // set rectangle
            ExplicitValueProvider * pValueProvider(
                ExplicitValueProvider::getExplicitValueProvider( m_xChartView ));
            if( pValueProvider )
                rOutEqualRect = pValueProvider->getRectangleOfObject( aCID, true );
        }

        return bResult;
    }

    // XSelectionSupplier (optional interface)
    sal_Bool SAL_CALL ChartController::select( const uno::Any& rSelection )
    {
        bool bSuccess = false;

        if ( rSelection.hasValue() )
        {
            const uno::Type& rType = rSelection.getValueType();
            if ( rType == cppu::UnoType< OUString >::get() )
            {
                OUString aNewCID;
                if ( ( rSelection >>= aNewCID ) && m_aSelection.setSelection( aNewCID ) )
                {
                    bSuccess = true;
                }
            }
            else if ( rType == cppu::UnoType<drawing::XShape>::get() )
            {
                uno::Reference< drawing::XShape > xShape;
                if ( ( rSelection >>= xShape ) && m_aSelection.setSelection( xShape ) )
                {
                    bSuccess = true;
                }
            }
        }
        else
        {
            if ( m_aSelection.hasSelection() )
            {
                m_aSelection.clearSelection();
                bSuccess = true;
            }
        }

        if ( bSuccess )
        {
            SolarMutexGuard aGuard;
            if ( m_pDrawViewWrapper && m_pDrawViewWrapper->IsTextEdit() )
            {
                EndTextEdit();
            }
            impl_selectObjectAndNotiy();
            auto pChartWindow(GetChartWindow());
            if ( pChartWindow )
            {
                pChartWindow->Invalidate();
            }
            return true;
        }

        return false;
    }

    uno::Any SAL_CALL ChartController::getSelection()
    {
        uno::Any aReturn;
        if ( m_aSelection.hasSelection() )
        {
            OUString aCID( m_aSelection.getSelectedCID() );
            if ( !aCID.isEmpty() )
            {
                aReturn <<= aCID;
            }
            else
            {
                // #i12587# support for shapes in chart
                aReturn <<= m_aSelection.getSelectedAdditionalShape();
            }
        }
        return aReturn;
    }

    void SAL_CALL ChartController::addSelectionChangeListener( const uno::Reference<view::XSelectionChangeListener> & xListener )
    {
        SolarMutexGuard aGuard;
        if( impl_isDisposedOrSuspended() )//@todo? allow adding of listeners in suspend mode?
            return; //behave passive if already disposed or suspended

        //--add listener
        m_aLifeTimeManager.m_aListenerContainer.addInterface( cppu::UnoType<view::XSelectionChangeListener>::get(), xListener );
    }

    void SAL_CALL ChartController::removeSelectionChangeListener( const uno::Reference<view::XSelectionChangeListener> & xListener )
    {
        SolarMutexGuard aGuard;
        if( impl_isDisposedOrSuspended() ) //@todo? allow removing of listeners in suspend mode?
            return; //behave passive if already disposed or suspended

        //--remove listener
        m_aLifeTimeManager.m_aListenerContainer.removeInterface( cppu::UnoType<view::XSelectionChangeListener>::get(), xListener );
    }

    void ChartController::impl_notifySelectionChangeListeners()
    {
        ::cppu::OInterfaceContainerHelper* pIC = m_aLifeTimeManager.m_aListenerContainer
            .getContainer( cppu::UnoType<view::XSelectionChangeListener>::get() );
        if( pIC )
        {
            uno::Reference< view::XSelectionSupplier > xSelectionSupplier(this);
            lang::EventObject aEvent( xSelectionSupplier );
            ::cppu::OInterfaceIteratorHelper aIt( *pIC );
            while( aIt.hasMoreElements() )
            {
                uno::Reference< view::XSelectionChangeListener > xListener( aIt.next(), uno::UNO_QUERY );
                if( xListener.is() )
                    xListener->selectionChanged( aEvent );
            }
        }
    }

    void ChartController::impl_selectObjectAndNotiy()
    {
        {
            SolarMutexGuard aGuard;
            DrawViewWrapper* pDrawViewWrapper = m_pDrawViewWrapper.get();
            if( pDrawViewWrapper )
            {
                pDrawViewWrapper->SetDragMode( m_eDragMode );
                m_aSelection.applySelection( m_pDrawViewWrapper.get() );
            }
        }
        impl_notifySelectionChangeListeners();
    }

    bool ChartController::impl_moveOrResizeObject(
        const OUString & rCID,
        eMoveOrResizeType eType,
        double fAmountLogicX,
        double fAmountLogicY )
    {
        bool bResult = false;
        bool bNeedResize = ( eType == CENTERED_RESIZE_OBJECT );

        uno::Reference< frame::XModel > xChartModel( getModel() );
        uno::Reference< beans::XPropertySet > xObjProp(
            ObjectIdentifier::getObjectPropertySet( rCID, xChartModel ));
        if( xObjProp.is())
        {
            awt::Size aRefSize = ChartModelHelper::getPageSize( xChartModel );

            chart2::RelativePosition aRelPos;
            chart2::RelativeSize     aRelSize;
            bool bDeterminePos  = !(xObjProp->getPropertyValue( "RelativePosition") >>= aRelPos);
            bool bDetermineSize = !bNeedResize || !(xObjProp->getPropertyValue( "RelativeSize") >>= aRelSize);

            if( ( bDeterminePos || bDetermineSize ) &&
                ( aRefSize.Width > 0 && aRefSize.Height > 0 ) )
            {
                ExplicitValueProvider * pValueProvider(
                    ExplicitValueProvider::getExplicitValueProvider( m_xChartView ));
                if( pValueProvider )
                {
                    awt::Rectangle aRect( pValueProvider->getRectangleOfObject( rCID ));
                    double fWidth = static_cast< double >( aRefSize.Width );
                    double fHeight = static_cast< double >( aRefSize.Height );
                    if( bDetermineSize )
                    {
                        aRelSize.Primary   = static_cast< double >( aRect.Width ) / fWidth;
                        aRelSize.Secondary = static_cast< double >( aRect.Height ) / fHeight;
                    }
                    if( bDeterminePos )
                    {
                        if( bNeedResize && aRelSize.Primary > 0.0 && aRelSize.Secondary > 0.0 )
                        {
                            aRelPos.Primary   = (static_cast< double >( aRect.X ) / fWidth) +
                                (aRelSize.Primary / 2.0);
                            aRelPos.Secondary = (static_cast< double >( aRect.Y ) / fHeight) +
                                (aRelSize.Secondary / 2.0);
                            aRelPos.Anchor = drawing::Alignment_CENTER;
                        }
                        else
                        {
                            aRelPos.Primary   = static_cast< double >( aRect.X ) / fWidth;
                            aRelPos.Secondary = static_cast< double >( aRect.Y ) / fHeight;
                            aRelPos.Anchor = drawing::Alignment_TOP_LEFT;
                        }
                    }
                }
            }

            if( eType == CENTERED_RESIZE_OBJECT )
                bResult = lcl_GrowAndShiftLogic( aRelPos, aRelSize, aRefSize, fAmountLogicX, fAmountLogicY );
            else if( eType == MOVE_OBJECT )
                bResult = lcl_MoveObjectLogic( aRelPos, aRelSize, aRefSize, fAmountLogicX, fAmountLogicY );

            if( bResult )
            {
                ActionDescriptionProvider::ActionType eActionType(ActionDescriptionProvider::ActionType::Move);
                if( bNeedResize )
                    eActionType = ActionDescriptionProvider::ActionType::Resize;

                ObjectType eObjectType = ObjectIdentifier::getObjectType( rCID );
                UndoGuard aUndoGuard( ActionDescriptionProvider::createDescription(
                        eActionType, ObjectNameProvider::getName( eObjectType )), m_xUndoManager );
                {
                    ControllerLockGuardUNO aCLGuard( xChartModel );
                    xObjProp->setPropertyValue( "RelativePosition", uno::Any( aRelPos ));
                    if( bNeedResize || (eObjectType == OBJECTTYPE_DIAGRAM) )//Also set an explicit size at the diagram when an explicit position is set
                        xObjProp->setPropertyValue( "RelativeSize", uno::Any( aRelSize ));
                }
                aUndoGuard.commit();
            }
        }
        return bResult;
    }

    bool ChartController::impl_DragDataPoint( const OUString & rCID, double fAdditionalOffset )
    {
        bool bResult = false;
        if( fAdditionalOffset < -1.0 || fAdditionalOffset > 1.0 || fAdditionalOffset == 0.0 )
            return bResult;

        sal_Int32 nDataPointIndex = ObjectIdentifier::getIndexFromParticleOrCID( rCID );
        uno::Reference< chart2::XDataSeries > xSeries(
            ObjectIdentifier::getDataSeriesForCID( rCID, getModel() ));
        if( xSeries.is())
        {
            try
            {
                uno::Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex( nDataPointIndex ));
                double fOffset = 0.0;
                if( xPointProp.is() &&
                    (xPointProp->getPropertyValue( "Offset" ) >>= fOffset ) &&
                    (( fAdditionalOffset > 0.0 && fOffset < 1.0 ) || (fOffset > 0.0)) )
                {
                    fOffset += fAdditionalOffset;
                    if( fOffset > 1.0 )
                        fOffset = 1.0;
                    else if( fOffset < 0.0 )
                        fOffset = 0.0;
                    xPointProp->setPropertyValue( "Offset", uno::Any( fOffset ));
                    bResult = true;
                }
            }
            catch( const uno::Exception & )
            {
                DBG_UNHANDLED_EXCEPTION("chart2");
            }
        }

        return bResult;
    }

    void ChartController::impl_SetMousePointer( const MouseEvent & rEvent )
    {
        SolarMutexGuard aGuard;
        auto pChartWindow(GetChartWindow());

        if (!m_pDrawViewWrapper || !pChartWindow)
            return;

        Point aMousePos( pChartWindow->PixelToLogic( rEvent.GetPosPixel()));
        sal_uInt16 nModifier = rEvent.GetModifier();
        bool bLeftDown = rEvent.IsLeft();

        // Check if object is for field button and set the normal arrow pointer in this case
        SdrObject* pObject = m_pDrawViewWrapper->getHitObject(aMousePos);
        if (pObject && pObject->GetName().startsWith("FieldButton"))
        {
            pChartWindow->SetPointer(Pointer(PointerStyle::Arrow));
            return;
        }

        if ( m_pDrawViewWrapper->IsTextEdit() )
        {
            if( m_pDrawViewWrapper->IsTextEditHit( aMousePos ) )
            {
                pChartWindow->SetPointer( m_pDrawViewWrapper->GetPreferredPointer(
                    aMousePos, pChartWindow, nModifier, bLeftDown ) );
                return;
            }
        }
        else if( m_pDrawViewWrapper->IsAction() )
        {
            return;//don't change pointer during running action
        }

        SdrHdl* pHitSelectionHdl = nullptr;
        if( m_aSelection.isResizeableObjectSelected() )
            pHitSelectionHdl = m_pDrawViewWrapper->PickHandle( aMousePos );

        if( pHitSelectionHdl )
        {
            Pointer aPointer = m_pDrawViewWrapper->GetPreferredPointer(
                aMousePos, pChartWindow, nModifier, bLeftDown );
            bool bForceArrowPointer = false;

            ObjectIdentifier aOID( m_aSelection.getSelectedOID() );

            switch( aPointer.GetStyle())
            {
                case PointerStyle::NSize:
                case PointerStyle::SSize:
                case PointerStyle::WSize:
                case PointerStyle::ESize:
                case PointerStyle::NWSize:
                case PointerStyle::NESize:
                case PointerStyle::SWSize:
                case PointerStyle::SESize:
                    if( ! m_aSelection.isResizeableObjectSelected() )
                        bForceArrowPointer = true;
                    break;
                case PointerStyle::Move:
                    if ( !aOID.isDragableObject() )
                        bForceArrowPointer = true;
                    break;
                case PointerStyle::MovePoint:
                case PointerStyle::MoveBezierWeight:
                    // there is no point-editing in a chart
                    // the PointerStyle::MoveBezierWeight appears in 3d data points
                    bForceArrowPointer = true;
                    break;
                default:
                    break;
            }

            if( bForceArrowPointer )
                pChartWindow->SetPointer( Pointer( PointerStyle::Arrow ));
            else
                pChartWindow->SetPointer( aPointer );

            return;
        }

        // #i12587# support for shapes in chart
        if ( m_eDrawMode == CHARTDRAW_INSERT &&
             ( !m_pDrawViewWrapper->IsMarkedHit( aMousePos ) || !m_aSelection.isDragableObjectSelected() ) )
        {
            PointerStyle ePointerStyle = PointerStyle::DrawRect;
            SdrObjKind eKind = static_cast< SdrObjKind >( m_pDrawViewWrapper->GetCurrentObjIdentifier() );
            switch ( eKind )
            {
                case OBJ_LINE:
                    {
                        ePointerStyle = PointerStyle::DrawLine;
                    }
                    break;
                case OBJ_RECT:
                case OBJ_CUSTOMSHAPE:
                    {
                        ePointerStyle = PointerStyle::DrawRect;
                    }
                    break;
                case OBJ_CIRC:
                    {
                        ePointerStyle = PointerStyle::DrawEllipse;
                    }
                    break;
                case OBJ_FREELINE:
                    {
                        ePointerStyle = PointerStyle::DrawPolygon;
                    }
                    break;
                case OBJ_TEXT:
                    {
                        ePointerStyle = PointerStyle::DrawText;
                    }
                    break;
                case OBJ_CAPTION:
                    {
                        ePointerStyle = PointerStyle::DrawCaption;
                    }
                    break;
                default:
                    {
                        ePointerStyle = PointerStyle::DrawRect;
                    }
                    break;
            }
            pChartWindow->SetPointer( Pointer( ePointerStyle ) );
            return;
        }

        OUString aHitObjectCID(
            SelectionHelper::getHitObjectCID(
                aMousePos, *m_pDrawViewWrapper, true /*bGetDiagramInsteadOf_Wall*/ ));

        if( m_pDrawViewWrapper->IsTextEdit() )
        {
            if( aHitObjectCID == m_aSelection.getSelectedCID() )
            {
                pChartWindow->SetPointer( Pointer( PointerStyle::Arrow ));
                return;
            }
        }

        if( aHitObjectCID.isEmpty() )
        {
            //additional shape was hit
            pChartWindow->SetPointer( PointerStyle::Move );
        }
        else if( ObjectIdentifier::isDragableObject( aHitObjectCID ) )
        {
            if( (m_eDragMode == SdrDragMode::Rotate)
                && SelectionHelper::isRotateableObject( aHitObjectCID
                    , getModel() ) )
                pChartWindow->SetPointer( Pointer( PointerStyle::Rotate ) );
            else
            {
                ObjectType eHitObjectType = ObjectIdentifier::getObjectType( aHitObjectCID );
                if( eHitObjectType == OBJECTTYPE_DATA_POINT )
                {
                    if( !ObjectIdentifier::areSiblings(aHitObjectCID,m_aSelection.getSelectedCID())
                        && !ObjectIdentifier::areIdenticalObjects(aHitObjectCID,m_aSelection.getSelectedCID()) )
                    {
                        pChartWindow->SetPointer( Pointer( PointerStyle::Arrow ));
                        return;
                    }
                }
                pChartWindow->SetPointer( PointerStyle::Move );
            }
        }
        else
            pChartWindow->SetPointer( Pointer( PointerStyle::Arrow ));
    }

    css::uno::Reference<css::uno::XInterface> const & ChartController::getChartView()
    {
        return m_xChartView;
    }

    void ChartController::sendPopupRequest(OUString const & rCID, tools::Rectangle aRectangle)
    {
        ChartModel* pChartModel = dynamic_cast<ChartModel*>(m_aModel->getModel().get());
        if (!pChartModel)
            return;

        uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider;
        xPivotTableDataProvider.set(pChartModel->getDataProvider(), uno::UNO_QUERY);
        if (!xPivotTableDataProvider.is())
            return;

        OUString sPivotTableName = xPivotTableDataProvider->getPivotTableName();

        PopupRequest* pPopupRequest = dynamic_cast<PopupRequest*>(pChartModel->getPopupRequest().get());
        if (!pPopupRequest)
            return;

        // Get dimension index from CID
        sal_Int32 nStartPos = rCID.lastIndexOf('.');
        nStartPos++;
        sal_Int32 nEndPos = rCID.getLength();
        OUString sDimensionIndex = rCID.copy(nStartPos, nEndPos - nStartPos);
        sal_Int32 nDimensionIndex = sDimensionIndex.toInt32();

        awt::Rectangle xRectangle {
            sal_Int32(aRectangle.Left()),
            sal_Int32(aRectangle.Top()),
            sal_Int32(aRectangle.GetWidth()),
            sal_Int32(aRectangle.GetHeight())
        };

        uno::Sequence<beans::PropertyValue> aCallbackData = comphelper::InitPropertySequence(
        {
            {"Rectangle",      uno::makeAny<awt::Rectangle>(xRectangle)},
            {"DimensionIndex", uno::makeAny<sal_Int32>(nDimensionIndex)},
            {"PivotTableName", uno::makeAny<OUString>(sPivotTableName)},
        });

        pPopupRequest->getCallback()->notify(uno::makeAny(aCallbackData));
    }

    } //namespace chart

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V522 There might be dereferencing of a potential null pointer 'pModel'.

↑ V571 Recurring check. The 'pChartWindow' condition was already verified in line 1435.

↑ V1019 Compound assignment expression is used inside condition.

↑ V571 Recurring check. The 'pChartWindow' condition was already verified in line 1465.

↑ V1019 Compound assignment expression is used inside condition.