diff options
Diffstat (limited to 'chart2/source/controller/main/ChartController.cxx')
-rw-r--r-- | chart2/source/controller/main/ChartController.cxx | 1688 |
1 files changed, 1688 insertions, 0 deletions
diff --git a/chart2/source/controller/main/ChartController.cxx b/chart2/source/controller/main/ChartController.cxx new file mode 100644 index 000000000..9174ef885 --- /dev/null +++ b/chart2/source/controller/main/ChartController.cxx @@ -0,0 +1,1688 @@ +/* -*- 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 <memory> +#include <sal/config.h> + +#include <config_wasm_strip.h> +#include <ChartController.hxx> +#include <servicenames.hxx> +#include <ResId.hxx> +#include <dlg_DataSource.hxx> +#include <ChartModel.hxx> +#include <ChartModelHelper.hxx> +#include <ChartType.hxx> +#include "ControllerCommandDispatch.hxx" +#include <DataSeries.hxx> +#include <Diagram.hxx> +#include <strings.hrc> +#include <chartview/ExplicitValueProvider.hxx> +#include <ChartViewHelper.hxx> + +#include <ChartWindow.hxx> +#include <chartview/DrawModelWrapper.hxx> +#include <DrawViewWrapper.hxx> +#include <ObjectIdentifier.hxx> +#include <DiagramHelper.hxx> +#include <ControllerLockGuard.hxx> +#include "UndoGuard.hxx" +#include "ChartDropTargetHelper.hxx" + +#include <dlg_ChartType.hxx> +#if !ENABLE_WASM_STRIP_ACCESSIBILITY +#include <AccessibleChartView.hxx> +#endif +#include "DrawCommandDispatch.hxx" +#include "ShapeController.hxx" +#include "UndoActions.hxx" +#include <ViewElementListProvider.hxx> + +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/servicehelper.hxx> +#include <BaseCoordinateSystem.hxx> + +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/frame/XController2.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <com/sun/star/util/XModeChangeBroadcaster.hpp> +#include <com/sun/star/frame/LayoutManagerEvents.hpp> +#include <com/sun/star/frame/XLayoutManagerEventBroadcaster.hpp> +#include <com/sun/star/ui/XSidebar.hpp> +#include <com/sun/star/chart2/XDataProviderAccess.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> + +#include <sal/log.hxx> +#include <tools/debug.hxx> +#include <svx/sidebar/SelectionChangeHandler.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <osl/mutex.hxx> +#include <comphelper/lok.hxx> + +#include <sfx2/sidebar/SidebarController.hxx> + +#include <com/sun/star/frame/XLayoutManager.hpp> + +// this is needed to properly destroy the unique_ptr to the AcceleratorExecute +// object in the DTOR +#include <svtools/acceleratorexecute.hxx> +#include <svx/ActionDescriptionProvider.hxx> +#include <tools/diagnose_ex.h> + +// enable the following define to let the controller listen to model changes and +// react on this by rebuilding the view +#define TEST_ENABLE_MODIFY_LISTENER + +namespace chart +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::chart2; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +ChartController::ChartController(uno::Reference<uno::XComponentContext> const & xContext) : + m_aLifeTimeManager( nullptr ), + m_bSuspended( false ), + m_xCC(xContext), + m_aModel( nullptr, m_aModelMutex ), + m_eDragMode(SdrDragMode::Move), + m_aDoubleClickTimer("chart2 ChartController m_aDoubleClickTimer"), + m_bWaitingForDoubleClick(false), + m_bWaitingForMouseUp(false), + m_bFieldButtonDown(false), + m_bConnectingToView(false), + m_bDisposed(false), + m_aDispatchContainer( m_xCC ), + m_eDrawMode( CHARTDRAW_SELECT ), + mpSelectionChangeHandler(new svx::sidebar::SelectionChangeHandler( + [this]() { return this->GetContextName(); }, + this, vcl::EnumContext::Context::Cell)) +{ + m_aDoubleClickTimer.SetInvokeHandler( LINK( this, ChartController, DoubleClickWaitingHdl ) ); +} + +ChartController::~ChartController() +{ + stopDoubleClickWaiting(); +} + +ChartController::TheModel::TheModel( const rtl::Reference<::chart::ChartModel> & xModel ) : + m_xModel( xModel ), + m_bOwnership( true ) +{ +} + +ChartController::TheModel::~TheModel() +{ +} + +void ChartController::TheModel::addListener( ChartController* pController ) +{ + if(m_xModel) + { + //if you need to be able to veto against the destruction of the model + // you must add as a close listener + + //otherwise you 'can' add as closelistener or 'must' add as dispose event listener + + m_xModel->addCloseListener( + static_cast<util::XCloseListener*>(pController) ); + } +} + +void ChartController::TheModel::removeListener( ChartController* pController ) +{ + if(m_xModel) + m_xModel->removeCloseListener( + static_cast<util::XCloseListener*>(pController) ); +} + +void ChartController::TheModel::tryTermination() +{ + if(!m_bOwnership) + return; + + try + { + if(m_xModel.is()) + { + try + { + //@todo ? are we allowed to use sal_True here if we have the explicit ownership? + //I think yes, because there might be other CloseListeners later in the list which might be interested still + //but make sure that we do not throw the CloseVetoException here ourselves + //so stop listening before trying to terminate or check the source of queryclosing event + m_xModel->close(true); + + m_bOwnership = false; + } + catch( const util::CloseVetoException& ) + { + //since we have indicated to give up the ownership with parameter true in close call + //the one who has thrown the CloseVetoException is the new owner + + SAL_WARN_IF( m_bOwnership, "chart2.main", "a well known owner has caught a CloseVetoException after calling close(true)"); + m_bOwnership = false; + return; + } + + } + } + catch(const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION( "chart2", "Termination of model failed" ); + } +} + +ChartController::TheModelRef::TheModelRef( TheModel* pTheModel, osl::Mutex& rMutex ) : + m_rModelMutex(rMutex) +{ + osl::Guard< osl::Mutex > aGuard( m_rModelMutex ); + m_xTheModel = pTheModel; +} +ChartController::TheModelRef::TheModelRef( const TheModelRef& rTheModel, ::osl::Mutex& rMutex ) : + m_rModelMutex(rMutex) +{ + osl::Guard< osl::Mutex > aGuard( m_rModelMutex ); + m_xTheModel = rTheModel.m_xTheModel; +} +ChartController::TheModelRef& ChartController::TheModelRef::operator=(TheModel* pTheModel) +{ + osl::Guard< osl::Mutex > aGuard( m_rModelMutex ); + m_xTheModel = pTheModel; + return *this; +} +ChartController::TheModelRef& ChartController::TheModelRef::operator=(const TheModelRef& rTheModel) +{ + osl::Guard< osl::Mutex > aGuard( m_rModelMutex ); + m_xTheModel = rTheModel.operator->(); + return *this; +} +ChartController::TheModelRef::~TheModelRef() +{ + osl::Guard< osl::Mutex > aGuard( m_rModelMutex ); + m_xTheModel.clear(); +} +bool ChartController::TheModelRef::is() const +{ + return m_xTheModel.is(); +} + +namespace { + +rtl::Reference<ChartType> getChartType(const rtl::Reference<ChartModel>& xChartDoc) +{ + rtl::Reference<Diagram > xDiagram = xChartDoc->getFirstChartDiagram(); + if (!xDiagram.is()) + return nullptr; + + const std::vector< rtl::Reference< BaseCoordinateSystem > > & xCooSysSequence( xDiagram->getBaseCoordinateSystems()); + if (xCooSysSequence.empty()) + return nullptr; + + return xCooSysSequence[0]->getChartTypes2()[0]; +} + +} + +OUString ChartController::GetContextName() +{ + if (m_bDisposed) + return OUString(); + + uno::Any aAny = getSelection(); + if (!aAny.hasValue()) + return "Chart"; + + OUString aCID; + aAny >>= aCID; + + if (aCID.isEmpty()) + return "Chart"; + + ObjectType eObjectID = ObjectIdentifier::getObjectType(aCID); + switch (eObjectID) + { + case OBJECTTYPE_DATA_SERIES: + return "Series"; + case OBJECTTYPE_DATA_ERRORS_X: + case OBJECTTYPE_DATA_ERRORS_Y: + case OBJECTTYPE_DATA_ERRORS_Z: + return "ErrorBar"; + case OBJECTTYPE_AXIS: + return "Axis"; + case OBJECTTYPE_GRID: + return "Grid"; + case OBJECTTYPE_DIAGRAM: + { + rtl::Reference<ChartType> xChartType = getChartType(getChartModel()); + if (xChartType.is() && xChartType->getChartType() == "com.sun.star.chart2.PieChartType") + return "ChartElements"; + break; + } + case OBJECTTYPE_DATA_CURVE: + case OBJECTTYPE_DATA_AVERAGE_LINE: + return "Trendline"; + default: + break; + } + + return "Chart"; +} + +// private methods + +bool ChartController::impl_isDisposedOrSuspended() const +{ + if( m_aLifeTimeManager.impl_isDisposed() ) + return true; + + if( m_bSuspended ) + { + OSL_FAIL( "This Controller is suspended" ); + return true; + } + return false; +} + +// lang::XServiceInfo + +OUString SAL_CALL ChartController::getImplementationName() +{ + return CHART_CONTROLLER_SERVICE_IMPLEMENTATION_NAME; +} + +sal_Bool SAL_CALL ChartController::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL ChartController::getSupportedServiceNames() +{ + return { + CHART_CONTROLLER_SERVICE_NAME, + "com.sun.star.frame.Controller" + //// @todo : add additional services if you support any further + }; +} + +namespace { + +uno::Reference<ui::XSidebar> getSidebarFromModel(const uno::Reference<frame::XModel>& xModel) +{ + uno::Reference<container::XChild> xChild(xModel, uno::UNO_QUERY); + if (!xChild.is()) + return nullptr; + + uno::Reference<frame::XModel> xParent (xChild->getParent(), uno::UNO_QUERY); + if (!xParent.is()) + return nullptr; + + uno::Reference<frame::XController2> xController(xParent->getCurrentController(), uno::UNO_QUERY); + if (!xController.is()) + return nullptr; + + uno::Reference<ui::XSidebarProvider> xSidebarProvider = xController->getSidebar(); + if (!xSidebarProvider.is()) + return nullptr; + + return xSidebarProvider->getSidebar(); +} + +} + +// XController + +void SAL_CALL ChartController::attachFrame( + const uno::Reference<frame::XFrame>& xFrame ) +{ + SolarMutexGuard aGuard; + + if( impl_isDisposedOrSuspended() ) //@todo? allow attaching the frame while suspended? + return; //behave passive if already disposed or suspended + + if(m_xFrame.is()) //what happens, if we do have a Frame already?? + { + //@todo? throw exception? + OSL_FAIL( "there is already a frame attached to the controller" ); + return; + } + + //--attach frame + m_xFrame = xFrame; //the frameloader is responsible to call xFrame->setComponent + + // Only notify after setting the frame, otherwise notification will fail + mpSelectionChangeHandler->Connect(); + + uno::Reference<ui::XSidebar> xSidebar = getSidebarFromModel(getChartModel()); + if (xSidebar.is()) + { + auto pSidebar = dynamic_cast<sfx2::sidebar::SidebarController*>(xSidebar.get()); + assert(pSidebar); + sfx2::sidebar::SidebarController::registerSidebarForFrame(pSidebar, this); + pSidebar->updateModel(getChartModel()); + css::lang::EventObject aEvent; + mpSelectionChangeHandler->selectionChanged(aEvent); + } + + //add as disposelistener to the frame (due to persistent reference) ??...: + + //the frame is considered to be owner of this controller and will live longer than we do + //the frame or the disposer of the frame has the duty to call suspend and dispose on this object + //so we do not need to add as lang::XEventListener for DisposingEvents right? + + //@todo nothing right??? + + //create view @todo is this the correct place here?? + + vcl::Window* pParent = nullptr; + //get the window parent from the frame to use as parent for our new window + if(xFrame.is()) + { + uno::Reference<awt::XWindow> xContainerWindow = xFrame->getContainerWindow(); + if (xContainerWindow) + xContainerWindow->setVisible(true); + pParent = VCLUnoHelper::GetWindow( xContainerWindow ); + } + + { + // calls to VCL + SolarMutexGuard aSolarGuard; + auto pChartWindow = VclPtr<ChartWindow>::Create(this,pParent,pParent?pParent->GetStyle():0); + pChartWindow->SetBackground();//no Background + m_xViewWindow.set( pChartWindow->GetComponentInterface(), uno::UNO_QUERY ); + pChartWindow->Show(); + m_apDropTargetHelper.reset( + new ChartDropTargetHelper( pChartWindow->GetDropTarget(), getChartModel())); + + impl_createDrawViewController(); + } + + //create the menu + { + uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY ); + if( xPropSet.is() ) + { + try + { + uno::Reference< css::frame::XLayoutManager > xLayoutManager; + xPropSet->getPropertyValue( "LayoutManager" ) >>= xLayoutManager; + if ( xLayoutManager.is() ) + { + xLayoutManager->lock(); + xLayoutManager->requestElement( "private:resource/menubar/menubar" ); + //@todo: createElement should become unnecessary, remove when #i79198# is fixed + xLayoutManager->createElement( "private:resource/toolbar/standardbar" ); + xLayoutManager->requestElement( "private:resource/toolbar/standardbar" ); + //@todo: createElement should become unnecessary, remove when #i79198# is fixed + xLayoutManager->createElement( "private:resource/toolbar/toolbar" ); + xLayoutManager->requestElement( "private:resource/toolbar/toolbar" ); + + // #i12587# support for shapes in chart + xLayoutManager->createElement( "private:resource/toolbar/drawbar" ); + xLayoutManager->requestElement( "private:resource/toolbar/drawbar" ); + + xLayoutManager->requestElement( "private:resource/statusbar/statusbar" ); + xLayoutManager->unlock(); + + // add as listener to get notified when + m_xLayoutManagerEventBroadcaster.set( xLayoutManager, uno::UNO_QUERY ); + if( m_xLayoutManagerEventBroadcaster.is()) + m_xLayoutManagerEventBroadcaster->addLayoutManagerEventListener( this ); + } + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + } + } +} + +//XModeChangeListener +void SAL_CALL ChartController::modeChanged( const util::ModeChangeEvent& rEvent ) +{ + SolarMutexGuard aGuard; + auto pChartWindow(GetChartWindow()); + //adjust controller to view status changes + + if( rEvent.NewMode == "dirty" ) + { + //the view has become dirty, we should repaint it if we have a window + if( pChartWindow ) + pChartWindow->ForceInvalidate(); + } + else if( rEvent.NewMode == "invalid" ) + { + //the view is about to become invalid so end all actions on it + impl_invalidateAccessible(); + if( m_pDrawViewWrapper && m_pDrawViewWrapper->IsTextEdit() ) + this->EndTextEdit(); + if( m_pDrawViewWrapper ) + { + m_pDrawViewWrapper->UnmarkAll(); + m_pDrawViewWrapper->HideSdrPage(); + } + } + else + { + //the view was rebuild so we can start some actions on it again + if( !m_bConnectingToView ) + { + if(pChartWindow && m_aModel.is() ) + { + m_bConnectingToView = true; + + GetDrawModelWrapper(); + if(m_pDrawModelWrapper) + { + { + if( m_pDrawViewWrapper ) + m_pDrawViewWrapper->ReInit(); + } + + //reselect object + if( m_aSelection.hasSelection() ) + this->impl_selectObjectAndNotiy(); + else + ChartModelHelper::triggerRangeHighlighting( getChartModel() ); + + impl_initializeAccessible(); + + { + if( pChartWindow ) + pChartWindow->Invalidate(); + } + } + + m_bConnectingToView = false; + } + } + } +} + +sal_Bool SAL_CALL ChartController::attachModel( const uno::Reference< frame::XModel > & xModel ) +{ + impl_invalidateAccessible(); + + //is called to attach the controller to a new model. + //return true if attach was successfully, false otherwise (e.g. if you do not work with a model) + + SolarMutexResettableGuard aGuard; + if( impl_isDisposedOrSuspended() ) //@todo? allow attaching a new model while suspended? + return false; //behave passive if already disposed or suspended + aGuard.clear(); + + ::chart::ChartModel* pChartModel = dynamic_cast<::chart::ChartModel*>(xModel.get()); + assert(!xModel || pChartModel); + + TheModelRef aNewModelRef( new TheModel(pChartModel), m_aModelMutex); + TheModelRef aOldModelRef(m_aModel,m_aModelMutex); + m_aModel = aNewModelRef; + + //--handle relations to the old model if any + if( aOldModelRef.is() ) + { + uno::Reference< util::XModeChangeBroadcaster > xViewBroadcaster( m_xChartView, uno::UNO_QUERY ); + if( xViewBroadcaster.is() ) + xViewBroadcaster->removeModeChangeListener(this); + m_pDrawModelWrapper.reset(); + + aOldModelRef->removeListener( this ); + #ifdef TEST_ENABLE_MODIFY_LISTENER + if( aOldModelRef->getModel().is()) + aOldModelRef->getModel()->removeModifyListener( this ); +#endif + } + + //--handle relations to the new model + aNewModelRef->addListener( this ); + + aGuard.reset(); // lock for m_aDispatchContainer access + // set new model at dispatchers + m_aDispatchContainer.setModel( aNewModelRef->getModel()); + rtl::Reference<ControllerCommandDispatch> pDispatch = new ControllerCommandDispatch( m_xCC, this, &m_aDispatchContainer ); + pDispatch->initialize(); + + // the dispatch container will return "this" for all commands returned by + // impl_getAvailableCommands(). That means, for those commands dispatch() + // is called here at the ChartController. + m_aDispatchContainer.setChartDispatch( pDispatch, o3tl::sorted_vector(impl_getAvailableCommands()) ); + + rtl::Reference<DrawCommandDispatch> pDrawDispatch = new DrawCommandDispatch( m_xCC, this ); + pDrawDispatch->initialize(); + m_aDispatchContainer.setDrawCommandDispatch( pDrawDispatch.get() ); + + rtl::Reference<ShapeController> pShapeController = new ShapeController( m_xCC, this ); + pShapeController->initialize(); + m_aDispatchContainer.setShapeController( pShapeController.get() ); + aGuard.clear(); + +#ifdef TEST_ENABLE_MODIFY_LISTENER + if( aNewModelRef->getModel().is()) + aNewModelRef->getModel()->addModifyListener( this ); +#endif + + // #i119999# Do not do this per default to allow the user to deselect the chart OLE with a single press to ESC + // select chart area per default: + // select( uno::Any( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, OUString() ) ) ); + + rtl::Reference< ChartModel > xFact = getChartModel(); + if( xFact.is()) + { + m_xChartView = xFact->createInstance( CHART_VIEW_SERVICE_NAME ); + GetDrawModelWrapper(); + uno::Reference< util::XModeChangeBroadcaster > xViewBroadcaster( m_xChartView, uno::UNO_QUERY ); + if( xViewBroadcaster.is() ) + xViewBroadcaster->addModeChangeListener(this); + } + + //the frameloader is responsible to call xModel->connectController + { + SolarMutexGuard aGuard2; + auto pChartWindow(GetChartWindow()); + if( pChartWindow ) + pChartWindow->Invalidate(); + } + + m_xUndoManager.set( getChartModel()->getUndoManager(), uno::UNO_SET_THROW ); + + return true; +} + +uno::Reference< frame::XFrame > SAL_CALL ChartController::getFrame() +{ + //provides access to owner frame of this controller + //return the frame containing this controller + + return m_xFrame; +} + +uno::Reference< frame::XModel > SAL_CALL ChartController::getModel() +{ + return getChartModel(); +} + +rtl::Reference<::chart::ChartModel> ChartController::getChartModel() +{ + //provides access to currently attached model + //returns the currently attached model + + //return nothing, if you do not have a model + TheModelRef aModelRef( m_aModel, m_aModelMutex); + if(aModelRef.is()) + return aModelRef->getModel(); + + return nullptr; +} + +rtl::Reference<::chart::Diagram> ChartController::getFirstDiagram() +{ + return ChartModelHelper::findDiagram( getChartModel() ); +} + +uno::Any SAL_CALL ChartController::getViewData() +{ + //provides access to current view status + //set of data that can be used to restore the current view status at later time + // by using XController::restoreViewData() + + SolarMutexGuard aGuard; + if( impl_isDisposedOrSuspended() ) + return uno::Any(); //behave passive if already disposed or suspended //@todo? or throw an exception?? + + //-- collect current view state + uno::Any aRet; + //// @todo integrate specialized implementation + + return aRet; +} + +void SAL_CALL ChartController::restoreViewData( + const uno::Any& /* Value */ ) +{ + //restores the view status using the data gotten from a previous call to XController::getViewData() + + SolarMutexGuard aGuard; + if( impl_isDisposedOrSuspended() ) + return; //behave passive if already disposed or suspended //@todo? or throw an exception?? + + //// @todo integrate specialized implementation +} + +sal_Bool SAL_CALL ChartController::suspend( sal_Bool bSuspend ) +{ + //is called to prepare the controller for closing the view + //bSuspend==true: force the controller to suspend his work + //bSuspend==false try to reactivate the controller + //returns true if request was accepted and of course successfully finished, false otherwise + + //we may show dialogs here to ask the user for saving changes ... @todo? + + SolarMutexGuard aGuard; + if( m_aLifeTimeManager.impl_isDisposed() ) + return false; //behave passive if already disposed, return false because request was not accepted //@todo? correct + + if(bool(bSuspend) == m_bSuspended) + { + OSL_FAIL( "new suspend mode equals old suspend mode" ); + return true; + } + + //change suspend mode + m_bSuspended = bSuspend; + return true; +} + +void ChartController::impl_createDrawViewController() +{ + SolarMutexGuard aGuard; + if(!m_pDrawViewWrapper) + { + if( m_pDrawModelWrapper ) + { + bool bLokCalcGlobalRTL = false; + if(comphelper::LibreOfficeKit::isActive() && AllSettings::GetLayoutRTL()) + { + rtl::Reference< ChartModel > xChartModel = getChartModel(); + if (xChartModel.is()) + { + uno::Reference<css::sheet::XSpreadsheetDocument> xSSDoc(xChartModel->getParent(), uno::UNO_QUERY); + if (xSSDoc.is()) + bLokCalcGlobalRTL = true; + } + } + + m_pDrawViewWrapper.reset( new DrawViewWrapper(m_pDrawModelWrapper->getSdrModel(),GetChartWindow()->GetOutDev()) ); + m_pDrawViewWrapper->SetNegativeX(bLokCalcGlobalRTL); + m_pDrawViewWrapper->attachParentReferenceDevice( getChartModel() ); + } + } +} + +void ChartController::impl_deleteDrawViewController() +{ + if( m_pDrawViewWrapper ) + { + SolarMutexGuard aGuard; + if( m_pDrawViewWrapper->IsTextEdit() ) + this->EndTextEdit(); + m_pDrawViewWrapper.reset(); + } +} + +// XComponent (base of XController) + +void SAL_CALL ChartController::dispose() +{ + m_bDisposed = true; + + mpSelectionChangeHandler->selectionChanged(css::lang::EventObject()); + mpSelectionChangeHandler->Disconnect(); + + if (getModel().is()) + { + uno::Reference<ui::XSidebar> xSidebar = getSidebarFromModel(getChartModel()); + if (sfx2::sidebar::SidebarController* pSidebar = dynamic_cast<sfx2::sidebar::SidebarController*>(xSidebar.get())) + { + sfx2::sidebar::SidebarController::unregisterSidebarForFrame(pSidebar, this); + } + } + + try + { + //This object should release all resources and references in the + //easiest possible manner + //This object must notify all registered listeners using the method + //<member>XEventListener::disposing</member> + + //hold no mutex + if( !m_aLifeTimeManager.dispose() ) + return; + +// OSL_ENSURE( m_bSuspended, "dispose was called but controller is not suspended" ); + + this->stopDoubleClickWaiting(); + + //end range highlighting + if( m_aModel.is()) + { + uno::Reference< view::XSelectionChangeListener > xSelectionChangeListener; + rtl::Reference< ChartModel > xDataReceiver = getChartModel(); + if( xDataReceiver.is() ) + xSelectionChangeListener.set( xDataReceiver->getRangeHighlighter(), uno::UNO_QUERY ); + if( xSelectionChangeListener.is() ) + { + uno::Reference< frame::XController > xController( this ); + lang::EventObject aEvent( xController ); + xSelectionChangeListener->disposing( aEvent ); + } + } + + //--release all resources and references + { + uno::Reference< util::XModeChangeBroadcaster > xViewBroadcaster( m_xChartView, uno::UNO_QUERY ); + if( xViewBroadcaster.is() ) + xViewBroadcaster->removeModeChangeListener(this); + + impl_invalidateAccessible(); + SolarMutexGuard aSolarGuard; + impl_deleteDrawViewController(); + m_pDrawModelWrapper.reset(); + + m_apDropTargetHelper.reset(); + + //the accessible view is disposed within window destructor of m_pChartWindow + if(m_xViewWindow.is()) + m_xViewWindow->dispose(); //ChartWindow is deleted via UNO due to dispose of m_xViewWindow (triggered by Framework (Controller pretends to be XWindow also)) + m_xChartView.clear(); + } + + // remove as listener to layout manager events + if( m_xLayoutManagerEventBroadcaster.is()) + { + m_xLayoutManagerEventBroadcaster->removeLayoutManagerEventListener( this ); + m_xLayoutManagerEventBroadcaster.set( nullptr ); + } + + m_xFrame.clear(); + m_xUndoManager.clear(); + + TheModelRef aModelRef( m_aModel, m_aModelMutex); + m_aModel = nullptr; + + if( aModelRef.is()) + { + uno::Reference< frame::XModel > xModel( aModelRef->getModel() ); + if(xModel.is()) + xModel->disconnectController( uno::Reference< frame::XController >( this )); + + aModelRef->removeListener( this ); +#ifdef TEST_ENABLE_MODIFY_LISTENER + try + { + if( aModelRef->getModel().is()) + aModelRef->getModel()->removeModifyListener( this ); + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } +#endif + aModelRef->tryTermination(); + } + + //// @todo integrate specialized implementation + //e.g. release further resources and references + + SolarMutexGuard g; + m_aDispatchContainer.DisposeAndClear(); + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + assert(!m_xChartView.is()); + } + } + +void SAL_CALL ChartController::addEventListener( + const uno::Reference<lang::XEventListener>& 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<lang::XEventListener>::get(), xListener ); +} + +void SAL_CALL ChartController::removeEventListener( + const uno::Reference<lang::XEventListener>& xListener ) +{ + SolarMutexGuard aGuard; + if( m_aLifeTimeManager.impl_isDisposed(false) ) + return; //behave passive if already disposed or suspended + + //--remove listener + m_aLifeTimeManager.m_aListenerContainer.removeInterface( cppu::UnoType<lang::XEventListener>::get(), xListener ); +} + +// util::XCloseListener +void SAL_CALL ChartController::queryClosing( + const lang::EventObject& rSource, + sal_Bool /*bGetsOwnership*/ ) +{ + //do not use the m_aControllerMutex here because this call is not allowed to block + + TheModelRef aModelRef( m_aModel, m_aModelMutex); + + if( !aModelRef.is() ) + return; + + if( uno::Reference<XInterface>(static_cast<cppu::OWeakObject*>(aModelRef->getModel().get())) != rSource.Source ) + { + OSL_FAIL( "queryClosing was called on a controller from an unknown source" ); + return; + } + + //@ todo prepare to closing model -> don't start any further hindering actions +} + +void SAL_CALL ChartController::notifyClosing( + const lang::EventObject& rSource ) +{ + //Listener should deregister himself and release all references to the closing object. + + TheModelRef aModelRef( m_aModel, m_aModelMutex); + if( !impl_releaseThisModel( rSource.Source ) ) + return; + + //--stop listening to the closing model + aModelRef->removeListener( this ); + + // #i79087# If the model using this controller is closed, the frame is + // expected to be closed as well + Reference< util::XCloseable > xFrameCloseable( m_xFrame, uno::UNO_QUERY ); + if( xFrameCloseable.is()) + { + try + { + xFrameCloseable->close( false /* DeliverOwnership */ ); + m_xFrame.clear(); + } + catch( const util::CloseVetoException & ) + { + // closing was vetoed + } + } +} + +bool ChartController::impl_releaseThisModel( + const uno::Reference< uno::XInterface > & xModel ) +{ + bool bReleaseModel = false; + { + ::osl::Guard< ::osl::Mutex > aGuard( m_aModelMutex ); + if( m_aModel.is() && uno::Reference< uno::XInterface >(static_cast<cppu::OWeakObject*>(m_aModel->getModel().get())) == xModel ) + { + m_aModel = nullptr; + m_xUndoManager.clear(); + bReleaseModel = true; + } + } + if( bReleaseModel ) + { + SolarMutexGuard g; + m_aDispatchContainer.setModel( nullptr ); + } + return bReleaseModel; +} + +// util::XEventListener (base of XCloseListener) +void SAL_CALL ChartController::disposing( + const lang::EventObject& rSource ) +{ + if( !impl_releaseThisModel( rSource.Source )) + { + if( rSource.Source == m_xLayoutManagerEventBroadcaster ) + m_xLayoutManagerEventBroadcaster.set( nullptr ); + } +} + +void SAL_CALL ChartController::layoutEvent( + const lang::EventObject& aSource, + sal_Int16 eLayoutEvent, + const uno::Any& /* aInfo */ ) +{ + if( eLayoutEvent == frame::LayoutManagerEvents::MERGEDMENUBAR ) + { + Reference< frame::XLayoutManager > xLM( aSource.Source, uno::UNO_QUERY ); + if( xLM.is()) + { + xLM->createElement( "private:resource/statusbar/statusbar" ); + xLM->requestElement( "private:resource/statusbar/statusbar" ); + } + } +} + +// XDispatchProvider (required interface) + +namespace +{ + +bool lcl_isFormatObjectCommand( std::u16string_view aCommand ) +{ + return aCommand == u"MainTitle" + || aCommand == u"SubTitle" + || aCommand == u"XTitle" + || aCommand == u"YTitle" + || aCommand == u"ZTitle" + || aCommand == u"SecondaryXTitle" + || aCommand == u"SecondaryYTitle" + || aCommand == u"AllTitles" + || aCommand == u"DiagramAxisX" + || aCommand == u"DiagramAxisY" + || aCommand == u"DiagramAxisZ" + || aCommand == u"DiagramAxisA" + || aCommand == u"DiagramAxisB" + || aCommand == u"DiagramAxisAll" + || aCommand == u"DiagramGridXMain" + || aCommand == u"DiagramGridYMain" + || aCommand == u"DiagramGridZMain" + || aCommand == u"DiagramGridXHelp" + || aCommand == u"DiagramGridYHelp" + || aCommand == u"DiagramGridZHelp" + || aCommand == u"DiagramGridAll" + + || aCommand == u"DiagramWall" + || aCommand == u"DiagramFloor" + || aCommand == u"DiagramArea" + || aCommand == u"Legend" + + || aCommand == u"FormatWall" + || aCommand == u"FormatFloor" + || aCommand == u"FormatChartArea" + || aCommand == u"FormatLegend" + + || aCommand == u"FormatTitle" + || aCommand == u"FormatAxis" + || aCommand == u"FormatDataSeries" + || aCommand == u"FormatDataPoint" + || aCommand == u"FormatDataLabels" + || aCommand == u"FormatDataLabel" + || aCommand == u"FormatXErrorBars" + || aCommand == u"FormatYErrorBars" + || aCommand == u"FormatMeanValue" + || aCommand == u"FormatTrendline" + || aCommand == u"FormatTrendlineEquation" + || aCommand == u"FormatStockLoss" + || aCommand == u"FormatStockGain" + || aCommand == u"FormatMajorGrid" + || aCommand == u"FormatMinorGrid"; +} + +} // anonymous namespace + +uno::Reference<frame::XDispatch> SAL_CALL + ChartController::queryDispatch( + const util::URL& rURL, + const OUString& rTargetFrameName, + sal_Int32 /* nSearchFlags */) +{ + SolarMutexGuard aGuard; + + if ( !m_aLifeTimeManager.impl_isDisposed() && getModel().is() ) + { + if( !rTargetFrameName.isEmpty() && rTargetFrameName == "_self" ) + return m_aDispatchContainer.getDispatchForURL( rURL ); + } + return uno::Reference< frame::XDispatch > (); +} + +uno::Sequence<uno::Reference<frame::XDispatch > > + ChartController::queryDispatches( + const uno::Sequence<frame::DispatchDescriptor>& xDescripts ) +{ + SolarMutexGuard g; + + if ( !m_aLifeTimeManager.impl_isDisposed() ) + { + return m_aDispatchContainer.getDispatchesForURLs( xDescripts ); + } + return uno::Sequence<uno::Reference<frame::XDispatch > > (); +} + +// frame::XDispatch + +void SAL_CALL ChartController::dispatch( + const util::URL& rURL, + const uno::Sequence< beans::PropertyValue >& rArgs ) +{ + OUString aCommand = rURL.Path; + + if(aCommand == "LOKSetTextSelection") + { + if (rArgs.getLength() == 3) + { + sal_Int32 nType = -1; + rArgs[0].Value >>= nType; + sal_Int32 nX = 0; + rArgs[1].Value >>= nX; + sal_Int32 nY = 0; + rArgs[2].Value >>= nY; + executeDispatch_LOKSetTextSelection(nType, nX, nY); + } + } + else if (aCommand == "LOKTransform") + { + if (rArgs[0].Name == "Action") + { + OUString sAction; + if ((rArgs[0].Value >>= sAction) && sAction == "PieSegmentDragging") + { + if (rArgs[1].Name == "Offset") + { + sal_Int32 nOffset; + if (rArgs[1].Value >>= nOffset) + { + this->executeDispatch_LOKPieSegmentDragging(nOffset); + } + } + } + } + else + { + this->executeDispatch_PositionAndSize(&rArgs); + } + } + else if(aCommand == "FillColor") + { + if (rArgs.getLength() > 0) + { + sal_uInt32 nColor; + if (rArgs[0].Value >>= nColor) + this->executeDispatch_FillColor(nColor); + } + } + else if(aCommand == "XLineColor") + { + if (rArgs.getLength() > 0) + { + sal_Int32 nColor = -1; + rArgs[0].Value >>= nColor; + this->executeDispatch_LineColor(nColor); + } + } + else if(aCommand == "LineWidth") + { + if (rArgs.getLength() > 0) + { + sal_Int32 nWidth = -1; + rArgs[0].Value >>= nWidth; + this->executeDispatch_LineWidth(nWidth); + } + } + else if(aCommand.startsWith("FillGradient")) + { + this->executeDispatch_FillGradient(aCommand.copy(aCommand.indexOf('=') + 1)); + } + else if(aCommand == "Paste") + this->executeDispatch_Paste(); + else if(aCommand == "Copy" ) + this->executeDispatch_Copy(); + else if(aCommand == "Cut" ) + this->executeDispatch_Cut(); + else if(aCommand == "DataRanges" ) + this->executeDispatch_SourceData(); + else if(aCommand == "Update" ) //Update Chart + { + ChartViewHelper::setViewToDirtyState( getChartModel() ); + SolarMutexGuard aGuard; + auto pChartWindow(GetChartWindow()); + if( pChartWindow ) + pChartWindow->Invalidate(); + } + else if(aCommand == "DiagramData" ) + this->executeDispatch_EditData(); + //insert objects + else if( aCommand == "InsertTitles" + || aCommand == "InsertMenuTitles") + this->executeDispatch_InsertTitles(); + else if( aCommand == "InsertMenuLegend" ) + this->executeDispatch_OpenLegendDialog(); + else if( aCommand == "InsertLegend" ) + this->executeDispatch_InsertLegend(); + else if( aCommand == "DeleteLegend" ) + this->executeDispatch_DeleteLegend(); + else if( aCommand == "InsertMenuDataLabels" ) + this->executeDispatch_InsertMenu_DataLabels(); + else if( aCommand == "InsertMenuAxes" + || aCommand == "InsertRemoveAxes" ) + this->executeDispatch_InsertAxes(); + else if( aCommand == "InsertMenuGrids" ) + this->executeDispatch_InsertGrid(); + else if( aCommand == "InsertMenuTrendlines" ) + this->executeDispatch_InsertMenu_Trendlines(); + else if( aCommand == "InsertMenuMeanValues" ) + this->executeDispatch_InsertMenu_MeanValues(); + else if( aCommand == "InsertMenuXErrorBars" ) + this->executeDispatch_InsertErrorBars(false); + else if( aCommand == "InsertMenuYErrorBars" ) + this->executeDispatch_InsertErrorBars(true); + else if( aCommand == "InsertSymbol" ) + this->executeDispatch_InsertSpecialCharacter(); + else if( aCommand == "InsertTrendline" ) + this->executeDispatch_InsertTrendline(); + else if( aCommand == "DeleteTrendline" ) + this->executeDispatch_DeleteTrendline(); + else if( aCommand == "InsertMeanValue" ) + this->executeDispatch_InsertMeanValue(); + else if( aCommand == "DeleteMeanValue" ) + this->executeDispatch_DeleteMeanValue(); + else if( aCommand == "InsertXErrorBars" ) + this->executeDispatch_InsertErrorBars(false); + else if( aCommand == "InsertYErrorBars" ) + this->executeDispatch_InsertErrorBars(true); + else if( aCommand == "DeleteXErrorBars" ) + this->executeDispatch_DeleteErrorBars(false); + else if( aCommand == "DeleteYErrorBars" ) + this->executeDispatch_DeleteErrorBars(true); + else if( aCommand == "InsertTrendlineEquation" ) + this->executeDispatch_InsertTrendlineEquation(); + else if( aCommand == "DeleteTrendlineEquation" ) + this->executeDispatch_DeleteTrendlineEquation(); + else if( aCommand == "InsertTrendlineEquationAndR2" ) + this->executeDispatch_InsertTrendlineEquation( true ); + else if( aCommand == "InsertR2Value" ) + this->executeDispatch_InsertR2Value(); + else if( aCommand == "DeleteR2Value") + this->executeDispatch_DeleteR2Value(); + else if( aCommand == "InsertDataLabels" ) + this->executeDispatch_InsertDataLabels(); + else if( aCommand == "InsertDataLabel" ) + this->executeDispatch_InsertDataLabel(); + else if( aCommand == "DeleteDataLabels") + this->executeDispatch_DeleteDataLabels(); + else if( aCommand == "DeleteDataLabel" ) + this->executeDispatch_DeleteDataLabel(); + else if( aCommand == "ResetAllDataPoints" ) + this->executeDispatch_ResetAllDataPoints(); + else if( aCommand == "ResetDataPoint" ) + this->executeDispatch_ResetDataPoint(); + else if( aCommand == "InsertAxis" ) + this->executeDispatch_InsertAxis(); + else if( aCommand == "InsertMajorGrid" ) + this->executeDispatch_InsertMajorGrid(); + else if( aCommand == "InsertMinorGrid" ) + this->executeDispatch_InsertMinorGrid(); + else if( aCommand == "InsertAxisTitle" ) + this->executeDispatch_InsertAxisTitle(); + else if( aCommand == "DeleteAxis" ) + this->executeDispatch_DeleteAxis(); + else if( aCommand == "DeleteMajorGrid") + this->executeDispatch_DeleteMajorGrid(); + else if( aCommand == "DeleteMinorGrid" ) + this->executeDispatch_DeleteMinorGrid(); + //format objects + else if( aCommand == "FormatSelection" ) + this->executeDispatch_ObjectProperties(); + else if( aCommand == "TransformDialog" ) + { + if ( isShapeContext() ) + { + this->impl_ShapeControllerDispatch( rURL, rArgs ); + } + else + { + this->executeDispatch_PositionAndSize(); + } + } + else if( lcl_isFormatObjectCommand(aCommand) ) + this->executeDispatch_FormatObject(rURL.Path); + //more format + else if( aCommand == "DiagramType" ) + this->executeDispatch_ChartType(); + else if( aCommand == "View3D" ) + this->executeDispatch_View3D(); + else if ( aCommand == "Forward" ) + { + if ( isShapeContext() ) + { + this->impl_ShapeControllerDispatch( rURL, rArgs ); + } + else + { + this->executeDispatch_MoveSeries( true ); + } + } + else if ( aCommand == "Backward" ) + { + if ( isShapeContext() ) + { + this->impl_ShapeControllerDispatch( rURL, rArgs ); + } + else + { + this->executeDispatch_MoveSeries( false ); + } + } + else if( aCommand == "NewArrangement") + this->executeDispatch_NewArrangement(); + else if( aCommand == "ToggleLegend" ) + this->executeDispatch_ToggleLegend(); + else if( aCommand == "ToggleGridHorizontal" ) + this->executeDispatch_ToggleGridHorizontal(); + else if( aCommand == "ToggleGridVertical" ) + this->executeDispatch_ToggleGridVertical(); + else if( aCommand == "ScaleText" ) + this->executeDispatch_ScaleText(); + else if( aCommand == "StatusBarVisible" ) + { + // workaround: this should not be necessary. + uno::Reference< beans::XPropertySet > xPropSet( m_xFrame, uno::UNO_QUERY ); + if( xPropSet.is() ) + { + uno::Reference< css::frame::XLayoutManager > xLayoutManager; + xPropSet->getPropertyValue( "LayoutManager" ) >>= xLayoutManager; + if ( xLayoutManager.is() ) + { + bool bIsVisible( xLayoutManager->isElementVisible( "private:resource/statusbar/statusbar" )); + if( bIsVisible ) + { + xLayoutManager->hideElement( "private:resource/statusbar/statusbar" ); + xLayoutManager->destroyElement( "private:resource/statusbar/statusbar" ); + } + else + { + xLayoutManager->createElement( "private:resource/statusbar/statusbar" ); + xLayoutManager->showElement( "private:resource/statusbar/statusbar" ); + } + // @todo: update menu state (checkmark next to "Statusbar"). + } + } + } +} + +void SAL_CALL ChartController::addStatusListener( + const uno::Reference<frame::XStatusListener >& /* xControl */, + const util::URL& /* aURL */ ) +{ + //@todo +} + +void SAL_CALL ChartController::removeStatusListener( + const uno::Reference<frame::XStatusListener >& /* xControl */, + const util::URL& /* aURL */ ) +{ + //@todo +} + +// XContextMenuInterception (optional interface) +void SAL_CALL ChartController::registerContextMenuInterceptor( + const uno::Reference< ui::XContextMenuInterceptor >& /* xInterceptor */) +{ + //@todo +} + +void SAL_CALL ChartController::releaseContextMenuInterceptor( + const uno::Reference< ui::XContextMenuInterceptor > & /* xInterceptor */) +{ + //@todo +} + +// ____ XEmbeddedClient ____ +// implementation see: ChartController_EditData.cxx + +void ChartController::executeDispatch_ChartType() +{ + UndoLiveUpdateGuard aUndoGuard( + SchResId( STR_ACTION_EDIT_CHARTTYPE ), m_xUndoManager ); + + SolarMutexGuard aSolarGuard; + //prepare and open dialog + ChartTypeDialog aDlg(GetChartFrame(), getChartModel()); + if (aDlg.run() == RET_OK) + { + impl_adaptDataSeriesAutoResize(); + aUndoGuard.commit(); + } +} + +void ChartController::executeDispatch_SourceData() +{ + //convert properties to ItemSet + rtl::Reference< ::chart::ChartModel > xChartDoc = getChartModel(); + OSL_ENSURE( xChartDoc.is(), "Invalid XChartDocument" ); + if( !xChartDoc.is() ) + return; + + // If there is a data table we should ask user if we really want to destroy it + // and switch to data ranges. + ChartModel& rModel = *xChartDoc; + if ( rModel.hasInternalDataProvider() ) + { + // Check if we will able to create data provider later + css::uno::Reference< com::sun::star::chart2::XDataProviderAccess > xCreatorDoc( + rModel.getParent(), uno::UNO_QUERY); + if (!xCreatorDoc.is()) + return; + + SolarMutexGuard aSolarGuard; + + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetChartFrame(), + VclMessageType::Question, VclButtonsType::YesNo, SchResId(STR_DLG_REMOVE_DATA_TABLE))); + // If "No" then just return + if (xQueryBox->run() == RET_NO) + return; + + // Remove data table + rModel.removeDataProviders(); + + // Ask parent document to create new data provider + + uno::Reference< data::XDataProvider > xDataProvider = xCreatorDoc->createDataProvider(); + SAL_WARN_IF( !xDataProvider.is(), "chart2.main", "Data provider was not created" ); + if (xDataProvider.is()) + { + rModel.attachDataProvider(xDataProvider); + } + } + + UndoLiveUpdateGuard aUndoGuard( + SchResId(STR_ACTION_EDIT_DATA_RANGES), m_xUndoManager); + + SolarMutexGuard aSolarGuard; + ::chart::DataSourceDialog aDlg(GetChartFrame(), xChartDoc); + if (aDlg.run() == RET_OK) + { + impl_adaptDataSeriesAutoResize(); + aUndoGuard.commit(); + } +} + +void ChartController::executeDispatch_MoveSeries( bool bForward ) +{ + ControllerLockGuardUNO aCLGuard( getChartModel() ); + + //get selected series + OUString aObjectCID(m_aSelection.getSelectedCID()); + rtl::Reference< DataSeries > xGivenDataSeries = ObjectIdentifier::getDataSeriesForCID( //yyy todo also legend entries and labels? + aObjectCID, getChartModel() ); + + UndoGuardWithSelection aUndoGuard( + ActionDescriptionProvider::createDescription( + (bForward ? ActionDescriptionProvider::ActionType::MoveToTop : ActionDescriptionProvider::ActionType::MoveToBottom), + SchResId(STR_OBJECT_DATASERIES)), + m_xUndoManager ); + + bool bChanged = DiagramHelper::moveSeries( getFirstDiagram(), xGivenDataSeries, bForward ); + if( bChanged ) + { + m_aSelection.setSelection( ObjectIdentifier::getMovedSeriesCID( aObjectCID, bForward ) ); + aUndoGuard.commit(); + } +} + +// ____ XMultiServiceFactory ____ +uno::Reference< uno::XInterface > SAL_CALL + ChartController::createInstance( const OUString& aServiceSpecifier ) +{ + uno::Reference< uno::XInterface > xResult; + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if( aServiceSpecifier == CHART_ACCESSIBLE_TEXT_SERVICE_NAME ) + xResult.set( impl_createAccessibleTextContext()); +#else + (void)aServiceSpecifier; +#endif + + return xResult; +} + +uno::Reference< uno::XInterface > SAL_CALL + ChartController::createInstanceWithArguments( + const OUString& ServiceSpecifier, + const uno::Sequence< uno::Any >& /* Arguments */ ) +{ + // ignore Arguments + return createInstance( ServiceSpecifier ); +} + +uno::Sequence< OUString > SAL_CALL + ChartController::getAvailableServiceNames() +{ + uno::Sequence< OUString > aServiceNames { CHART_ACCESSIBLE_TEXT_SERVICE_NAME }; + return aServiceNames; +} + +// ____ XModifyListener ____ +void SAL_CALL ChartController::modified( + const lang::EventObject& /* aEvent */ ) +{ + // the source can also be a subobject of the ChartModel + // @todo: change the source in ChartModel to always be the model itself ? + //todo? update menu states ? +} + +void ChartController::NotifyUndoActionHdl( std::unique_ptr<SdrUndoAction> pUndoAction ) +{ + ENSURE_OR_RETURN_VOID( pUndoAction, "invalid Undo action" ); + + OUString aObjectCID = m_aSelection.getSelectedCID(); + if ( !aObjectCID.isEmpty() ) + return; + + try + { + rtl::Reference< ChartModel > xSuppUndo = getChartModel(); + const Reference< document::XUndoManager > xUndoManager( xSuppUndo->getUndoManager(), uno::UNO_SET_THROW ); + const Reference< document::XUndoAction > xAction( new impl::ShapeUndoElement( std::move(pUndoAction) ) ); + xUndoManager->addUndoAction( xAction ); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } +} + +DrawModelWrapper* ChartController::GetDrawModelWrapper() +{ + if( !m_pDrawModelWrapper ) + { + ExplicitValueProvider* pProvider = comphelper::getFromUnoTunnel<ExplicitValueProvider>( m_xChartView ); + if( pProvider ) + m_pDrawModelWrapper = pProvider->getDrawModelWrapper(); + if ( m_pDrawModelWrapper ) + { + m_pDrawModelWrapper->getSdrModel().SetNotifyUndoActionHdl( + std::bind(&ChartController::NotifyUndoActionHdl, this, std::placeholders::_1) ); + } + } + return m_pDrawModelWrapper.get(); +} + +DrawViewWrapper* ChartController::GetDrawViewWrapper() +{ + if ( !m_pDrawViewWrapper ) + { + impl_createDrawViewController(); + } + return m_pDrawViewWrapper.get(); +} + + +ChartWindow* ChartController::GetChartWindow() const +{ + // clients getting the naked VCL Window from UNO should always have the + // solar mutex (and keep it over the lifetime of this ptr), as VCL might + // might deinit otherwise + DBG_TESTSOLARMUTEX(); + if(!m_xViewWindow.is()) + return nullptr; + return dynamic_cast<ChartWindow*>(VCLUnoHelper::GetWindow(m_xViewWindow)); +} + +weld::Window* ChartController::GetChartFrame() +{ + // clients getting the naked VCL Window from UNO should always have the + // solar mutex (and keep it over the lifetime of this ptr), as VCL might + // might deinit otherwise + DBG_TESTSOLARMUTEX(); + return Application::GetFrameWeld(m_xViewWindow); +} + +bool ChartController::isAdditionalShapeSelected() const +{ + return m_aSelection.isAdditionalShapeSelected(); +} + +void ChartController::SetAndApplySelection(const Reference<drawing::XShape>& rxShape) +{ + if(rxShape.is()) + { + m_aSelection.setSelection(rxShape); + m_aSelection.applySelection(GetDrawViewWrapper()); + } +} + + + +uno::Reference< XAccessible > ChartController::CreateAccessible() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + uno::Reference< XAccessible > xResult = new AccessibleChartView( GetDrawViewWrapper() ); + impl_initializeAccessible( uno::Reference< lang::XInitialization >( xResult, uno::UNO_QUERY ) ); + return xResult; +#else + return uno::Reference< XAccessible >(); +#endif +} + +void ChartController::impl_invalidateAccessible() +{ + SolarMutexGuard aGuard; + auto pChartWindow(GetChartWindow()); + if( pChartWindow ) + { + Reference< lang::XInitialization > xInit( pChartWindow->GetAccessible(false), uno::UNO_QUERY ); + if(xInit.is()) + { + uno::Sequence< uno::Any > aArguments(3);//empty arguments -> invalid accessible + xInit->initialize(aArguments); + } + } +} +void ChartController::impl_initializeAccessible() +{ + SolarMutexGuard aGuard; + auto pChartWindow(GetChartWindow()); + if( pChartWindow ) + this->impl_initializeAccessible( Reference< lang::XInitialization >( pChartWindow->GetAccessible(false), uno::UNO_QUERY ) ); +} +void ChartController::impl_initializeAccessible( const uno::Reference< lang::XInitialization >& xInit ) +{ + if(!xInit.is()) + return; + + uno::Reference< XAccessible > xParent; + { + SolarMutexGuard aGuard; + auto pChartWindow(GetChartWindow()); + if( pChartWindow ) + { + vcl::Window* pParentWin( pChartWindow->GetAccessibleParentWindow()); + if( pParentWin ) + xParent.set( pParentWin->GetAccessible()); + } + } + uno::Sequence< uno::Any > aArguments{ uno::Any(uno::Reference<view::XSelectionSupplier>(this)), + uno::Any(getModel()), + uno::Any(m_xChartView), + uno::Any(xParent), + uno::Any(m_xViewWindow) }; + + xInit->initialize(aArguments); +} + +const o3tl::sorted_vector< OUString >& ChartController::impl_getAvailableCommands() +{ + static const o3tl::sorted_vector< OUString > s_AvailableCommands { + // commands for container forward + "AddDirect", "NewDoc", "Open", + "Save", "SaveAs", "SendMail", + "EditDoc", "ExportDirectToPDF", "PrintDefault", + + // own commands + "Cut", "Copy", "Paste", + "DataRanges", "DiagramData", + // insert objects + "InsertMenuTitles", "InsertTitles", + "InsertMenuLegend", "InsertLegend", "DeleteLegend", + "InsertMenuDataLabels", + "InsertMenuAxes", "InsertRemoveAxes", "InsertMenuGrids", + "InsertSymbol", + "InsertTrendlineEquation", "InsertTrendlineEquationAndR2", + "InsertR2Value", "DeleteR2Value", + "InsertMenuTrendlines", "InsertTrendline", + "InsertMenuMeanValues", "InsertMeanValue", + "InsertMenuXErrorBars", "InsertXErrorBars", + "InsertMenuYErrorBars", "InsertYErrorBars", + "InsertDataLabels", "InsertDataLabel", + "DeleteTrendline", "DeleteMeanValue", "DeleteTrendlineEquation", + "DeleteXErrorBars", "DeleteYErrorBars", + "DeleteDataLabels", "DeleteDataLabel", + //format objects + "FormatSelection", "TransformDialog", + "DiagramType", "View3D", + "Forward", "Backward", + "MainTitle", "SubTitle", + "XTitle", "YTitle", "ZTitle", + "SecondaryXTitle", "SecondaryYTitle", + "AllTitles", "Legend", + "DiagramAxisX", "DiagramAxisY", "DiagramAxisZ", + "DiagramAxisA", "DiagramAxisB", "DiagramAxisAll", + "DiagramGridXMain", "DiagramGridYMain", "DiagramGridZMain", + "DiagramGridXHelp", "DiagramGridYHelp", "DiagramGridZHelp", + "DiagramGridAll", + "DiagramWall", "DiagramFloor", "DiagramArea", + + //context menu - format objects entries + "FormatWall", "FormatFloor", "FormatChartArea", + "FormatLegend", + + "FormatAxis", "FormatTitle", + "FormatDataSeries", "FormatDataPoint", + "ResetAllDataPoints", "ResetDataPoint", + "FormatDataLabels", "FormatDataLabel", + "FormatMeanValue", "FormatTrendline", "FormatTrendlineEquation", + "FormatXErrorBars", "FormatYErrorBars", + "FormatStockLoss", "FormatStockGain", + + "FormatMajorGrid", "InsertMajorGrid", "DeleteMajorGrid", + "FormatMinorGrid", "InsertMinorGrid", "DeleteMinorGrid", + "InsertAxis", "DeleteAxis", "InsertAxisTitle", + + // toolbar commands + "ToggleGridHorizontal", "ToggleGridVertical", "ToggleLegend", "ScaleText", + "NewArrangement", "Update", + "DefaultColors", "BarWidth", "NumberOfLines", + "ArrangeRow", + "StatusBarVisible", + "ChartElementSelector"}; + return s_AvailableCommands; +} + +ViewElementListProvider ChartController::getViewElementListProvider() +{ + return ViewElementListProvider(m_pDrawModelWrapper.get()); +} + +} //namespace chart + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_chart2_ChartController_get_implementation(css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new chart::ChartController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |