diff options
Diffstat (limited to 'chart2/source/controller/main')
48 files changed, 15356 insertions, 0 deletions
diff --git a/chart2/source/controller/main/ChartController.cxx b/chart2/source/controller/main/ChartController.cxx new file mode 100644 index 0000000000..666d6b6367 --- /dev/null +++ b/chart2/source/controller/main/ChartController.cxx @@ -0,0 +1,1655 @@ +/* -*- 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 <ChartView.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/XVclWindowPeer.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 <utility> +#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 <comphelper/diagnose_ex.hxx> + +// 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> xContext) : + m_aLifeTimeManager( nullptr ), + m_bSuspended( false ), + m_xCC(std::move(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( rtl::Reference<::chart::ChartModel> xModel ) : + m_xModel(std::move( 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; +} + +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); + pSidebar->registerSidebarForFrame(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() ) + { + if( m_xChartView.is() ) + m_xChartView->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 = dynamic_cast<::chart::ChartView*>(xFact->createInstance( CHART_VIEW_SERVICE_NAME ).get()); + GetDrawModelWrapper(); + m_xChartView->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 getChartModel()->getFirstChartDiagram(); +} + +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; +} + +// css::frame::XController2 + +css::uno::Reference<css::awt::XWindow> SAL_CALL ChartController::getComponentWindow() +{ + // it is a special characteristic of ChartController + // that it simultaneously provides the XWindow functionality + return this; +} + +OUString SAL_CALL ChartController::getViewControllerName() { return {}; } + +css::uno::Sequence<css::beans::PropertyValue> SAL_CALL ChartController::getCreationArguments() +{ + return {}; +} + +css::uno::Reference<css::ui::XSidebarProvider> SAL_CALL ChartController::getSidebar() { return {}; } + +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())) + { + pSidebar->unregisterSidebarForFrame(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 + { + if( m_xChartView.is() ) + m_xChartView->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.clear(); + } + + 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 ) +{ + if( impl_isDisposedOrSuspended() )//@todo? allow adding of listeners in suspend mode? + return; //behave passive if already disposed or suspended + + //--add listener + std::unique_lock aGuard2(m_aLifeTimeManager.m_aAccessMutex); + m_aLifeTimeManager.m_aEventListeners.addInterface( aGuard2, 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 + std::unique_lock aGuard2(m_aLifeTimeManager.m_aAccessMutex); + m_aLifeTimeManager.m_aEventListeners.removeInterface( aGuard2, 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.clear(); + } +} + +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.subView(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 == "InsertMenuDataTable" ) + this->executeDispatch_OpenInsertDataTableDialog(); + 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(); + else if( aCommand == "InsertDataTable" ) + this->executeDispatch_InsertDataTable(); + else if( aCommand == "DeleteDataTable" ) + this->executeDispatch_DeleteDataTable(); + //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 = getFirstDiagram()->moveSeries( xGivenDataSeries, bForward ); + if( bChanged ) + { + m_aSelection.setSelection( ObjectIdentifier::getMovedSeriesCID( aObjectCID, bForward ) ); + aUndoGuard.commit(); + } +} + +// ____ 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 ) + { + if( m_xChartView ) + m_pDrawModelWrapper = m_xChartView->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 + rtl::Reference< AccessibleChartView > xResult = new AccessibleChartView( GetDrawViewWrapper() ); + impl_initializeAccessible( *xResult ); + return xResult; +#else + return uno::Reference< XAccessible >(); +#endif +} + +void ChartController::impl_invalidateAccessible() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + SolarMutexGuard aGuard; + auto pChartWindow(GetChartWindow()); + if( pChartWindow ) + { + Reference< XInterface > xInit( pChartWindow->GetAccessible(false) ); + if(xInit.is()) + { + //empty arguments -> invalid accessible + dynamic_cast<AccessibleChartView&>(*xInit).initialize(); + } + } +#endif +} +void ChartController::impl_initializeAccessible() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + SolarMutexGuard aGuard; + auto pChartWindow(GetChartWindow()); + if( !pChartWindow ) + return; + Reference< XInterface > xInit( pChartWindow->GetAccessible(false) ); + if(xInit.is()) + impl_initializeAccessible( dynamic_cast<AccessibleChartView&>(*xInit) ); +#endif +} +#if !ENABLE_WASM_STRIP_ACCESSIBILITY +void ChartController::impl_initializeAccessible( AccessibleChartView& rAccChartView ) +{ + uno::Reference< XAccessible > xParent; + { + SolarMutexGuard aGuard; + auto pChartWindow(GetChartWindow()); + if( pChartWindow ) + { + vcl::Window* pParentWin( pChartWindow->GetAccessibleParentWindow()); + if( pParentWin ) + xParent.set( pParentWin->GetAccessible()); + } + } + + rAccChartView.initialize(*this, getChartModel(), m_xChartView, xParent, m_xViewWindow); +} +#else +void ChartController::impl_initializeAccessible( AccessibleChartView& /* rAccChartView */) {} +#endif + +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", + "InsertMenuDataTable", + "InsertDataTable", "DeleteDataTable", + //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 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartController_EditData.cxx b/chart2/source/controller/main/ChartController_EditData.cxx new file mode 100644 index 0000000000..63577edf39 --- /dev/null +++ b/chart2/source/controller/main/ChartController_EditData.cxx @@ -0,0 +1,52 @@ +/* -*- 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 <ChartModel.hxx> + +#include <dlg_DataEditor.hxx> +#include "UndoGuard.hxx" +#include <ResId.hxx> +#include <strings.hrc> + +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; + +namespace chart +{ + +void ChartController::executeDispatch_EditData() +{ + rtl::Reference<::chart::ChartModel> xChartDoc( getChartModel(), uno::UNO_QUERY ); + if (xChartDoc.is()) + { + SolarMutexGuard aSolarGuard; + UndoLiveUpdateGuardWithData aUndoGuard( + SchResId( STR_ACTION_EDIT_CHART_DATA ), + m_xUndoManager ); + DataEditor aDataEditorDialog(GetChartFrame(), xChartDoc, m_xCC); + aDataEditorDialog.run(); + aUndoGuard.commit(); + } +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartController_Insert.cxx b/chart2/source/controller/main/ChartController_Insert.cxx new file mode 100644 index 0000000000..60b059ceff --- /dev/null +++ b/chart2/source/controller/main/ChartController_Insert.cxx @@ -0,0 +1,987 @@ +/* -*- 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 <ChartController.hxx> +#include <ChartView.hxx> + +#include <dlg_InsertAxis_Grid.hxx> +#include <dlg_InsertDataLabel.hxx> +#include <dlg_InsertLegend.hxx> +#include <dlg_InsertErrorBars.hxx> +#include <dlg_InsertTitle.hxx> +#include <dlg_InsertDataTable.hxx> +#include <dlg_ObjectProperties.hxx> + +#include <Axis.hxx> +#include <AxisHelper.hxx> +#include <TitleHelper.hxx> +#include <DataSeries.hxx> +#include <DiagramHelper.hxx> +#include <Diagram.hxx> +#include <GridProperties.hxx> +#include <chartview/DrawModelWrapper.hxx> +#include <chartview/ChartSfxItemIds.hxx> +#include <NumberFormatterWrapper.hxx> +#include <ViewElementListProvider.hxx> +#include <MultipleChartConverters.hxx> +#include <ControllerLockGuard.hxx> +#include "UndoGuard.hxx" +#include <ResId.hxx> +#include <strings.hrc> +#include <ReferenceSizeProvider.hxx> +#include <ObjectIdentifier.hxx> +#include <RegressionCurveHelper.hxx> +#include <RegressionCurveItemConverter.hxx> +#include <StatisticsHelper.hxx> +#include <ErrorBarItemConverter.hxx> +#include <DataSeriesHelper.hxx> +#include <ObjectNameProvider.hxx> +#include <Legend.hxx> +#include <LegendHelper.hxx> +#include <DataTable.hxx> +#include <RegressionCurveModel.hxx> + +#include <com/sun/star/chart2/XRegressionCurve.hpp> +#include <com/sun/star/chart/ErrorBarStyle.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <svx/ActionDescriptionProvider.hxx> + +#include <comphelper/diagnose_ex.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace +{ + +void lcl_InsertMeanValueLine( const rtl::Reference< ::chart::DataSeries > & xSeries ) +{ + if( xSeries.is()) + { + ::chart::RegressionCurveHelper::addMeanValueLine( + xSeries, xSeries); + } +} + +} // anonymous namespace + +namespace chart +{ + +void ChartController::executeDispatch_InsertAxes() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_AXES )), + m_xUndoManager ); + + try + { + InsertAxisOrGridDialogData aDialogInput; + rtl::Reference< Diagram > xDiagram = getFirstDiagram(); + AxisHelper::getAxisOrGridExistence( aDialogInput.aExistenceList, xDiagram ); + AxisHelper::getAxisOrGridPossibilities( aDialogInput.aPossibilityList, xDiagram ); + + SolarMutexGuard aGuard; + SchAxisDlg aDlg(GetChartFrame(), aDialogInput); + if (aDlg.run() == RET_OK) + { + // lock controllers till end of block + ControllerLockGuardUNO aCLGuard( getChartModel() ); + + InsertAxisOrGridDialogData aDialogOutput; + aDlg.getResult(aDialogOutput); + std::unique_ptr< ReferenceSizeProvider > pRefSizeProvider( + impl_createReferenceSizeProvider()); + bool bChanged = AxisHelper::changeVisibilityOfAxes( xDiagram + , aDialogInput.aExistenceList, aDialogOutput.aExistenceList, m_xCC + , pRefSizeProvider.get() ); + if( bChanged ) + aUndoGuard.commit(); + } + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +void ChartController::executeDispatch_InsertGrid() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_GRIDS )), + m_xUndoManager ); + + try + { + InsertAxisOrGridDialogData aDialogInput; + rtl::Reference< Diagram > xDiagram = getFirstDiagram(); + AxisHelper::getAxisOrGridExistence( aDialogInput.aExistenceList, xDiagram, false ); + AxisHelper::getAxisOrGridPossibilities( aDialogInput.aPossibilityList, xDiagram, false ); + + SolarMutexGuard aGuard; + SchGridDlg aDlg(GetChartFrame(), aDialogInput);//aItemSet, b3D, bNet, bSecondaryX, bSecondaryY ); + if (aDlg.run() == RET_OK) + { + // lock controllers till end of block + ControllerLockGuardUNO aCLGuard( getChartModel() ); + InsertAxisOrGridDialogData aDialogOutput; + aDlg.getResult( aDialogOutput ); + bool bChanged = AxisHelper::changeVisibilityOfGrids( xDiagram + , aDialogInput.aExistenceList, aDialogOutput.aExistenceList ); + if( bChanged ) + aUndoGuard.commit(); + } + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +void ChartController::executeDispatch_OpenInsertDataTableDialog() +{ + SolarMutexGuard aGuard; + + auto aUndoDescription = ActionDescriptionProvider::createDescription(ActionDescriptionProvider::ActionType::Insert, SchResId(STR_DATA_TABLE)); + UndoGuard aUndoGuard(aUndoDescription, m_xUndoManager); + + rtl::Reference<Diagram> xDiagram = getFirstDiagram(); + + InsertDataTableDialog aDialog(GetChartFrame()); + { + // init values + DataTableDialogData aData; + auto xDataTable = xDiagram->getDataTable(); + aData.mbShow = xDataTable.is(); + if (xDataTable.is()) + { + uno::Reference<beans::XPropertySet> xProperties(xDataTable, uno::UNO_QUERY); + + uno::Any aAny = xProperties->getPropertyValue("HBorder"); + if (aAny.has<bool>()) + aData.mbHorizontalBorders = aAny.get<bool>(); + + aAny = xProperties->getPropertyValue("VBorder"); + if (aAny.has<bool>()) + aData.mbVerticalBorders = aAny.get<bool>(); + + aAny = xProperties->getPropertyValue("Outline"); + if (aAny.has<bool>()) + aData.mbOutline = aAny.get<bool>(); + + aAny = xProperties->getPropertyValue("Keys"); + if (aAny.has<bool>()) + aData.mbKeys = aAny.get<bool>(); + } + aDialog.init(aData); + } + + // show the dialog + if (aDialog.run() == RET_OK) + { + bool bChanged = false; + + auto& rDialogData = aDialog.getDataTableDialogData(); + auto xDataTable = xDiagram->getDataTable(); + if (!rDialogData.mbShow && xDataTable.is()) + { + xDiagram->setDataTable(uno::Reference<chart2::XDataTable>()); + bChanged = true; + } + else if (rDialogData.mbShow && !xDataTable.is()) + { + uno::Reference<chart2::XDataTable> xNewDataTable(new DataTable); + if (xNewDataTable.is()) + { + xDiagram->setDataTable(xNewDataTable); + bChanged = true; + } + } + + // Set the properties + xDataTable = xDiagram->getDataTable(); + if (rDialogData.mbShow && xDataTable.is()) + { + uno::Reference<beans::XPropertySet> xProperties(xDataTable, uno::UNO_QUERY); + xProperties->setPropertyValue("HBorder" , uno::Any(rDialogData.mbHorizontalBorders)); + xProperties->setPropertyValue("VBorder" , uno::Any(rDialogData.mbVerticalBorders)); + xProperties->setPropertyValue("Outline" , uno::Any(rDialogData.mbOutline)); + xProperties->setPropertyValue("Keys" , uno::Any(rDialogData.mbKeys)); + bChanged = true; + } + + if (bChanged) + aUndoGuard.commit(); + } +} + +/** Create and insert a data table to the chart */ +void ChartController::executeDispatch_InsertDataTable() +{ + auto aUndoDescription = ActionDescriptionProvider::createDescription(ActionDescriptionProvider::ActionType::Insert, SchResId(STR_DATA_TABLE)); + UndoGuard aUndoGuard(aUndoDescription, m_xUndoManager); + + + rtl::Reference<Diagram> xDiagram = getFirstDiagram(); + auto xDataTable = xDiagram->getDataTable(); + if (!xDataTable.is()) + { + uno::Reference<chart2::XDataTable> xNewDataTable(new DataTable); + if (xNewDataTable.is()) + { + xDiagram->setDataTable(xNewDataTable); + aUndoGuard.commit(); + } + } +} + +/** Delete a data table from the chart */ +void ChartController::executeDispatch_DeleteDataTable() +{ + auto aUndoDescription = ActionDescriptionProvider::createDescription(ActionDescriptionProvider::ActionType::Delete, SchResId(STR_DATA_TABLE)); + UndoGuard aUndoGuard(aUndoDescription, m_xUndoManager); + + rtl::Reference<Diagram> xDiagram = getFirstDiagram(); + auto xDataTable = xDiagram->getDataTable(); + if (xDataTable.is()) + { + // insert a empty data table reference + xDiagram->setDataTable(uno::Reference<chart2::XDataTable>()); + aUndoGuard.commit(); + } +} + +void ChartController::executeDispatch_InsertTitles() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_TITLES )), + m_xUndoManager ); + + try + { + TitleDialogData aDialogInput; + aDialogInput.readFromModel( getChartModel() ); + + SolarMutexGuard aGuard; + SchTitleDlg aDlg(GetChartFrame(), aDialogInput); + if (aDlg.run() == RET_OK) + { + // lock controllers till end of block + ControllerLockGuardUNO aCLGuard( getChartModel() ); + TitleDialogData aDialogOutput(impl_createReferenceSizeProvider()); + aDlg.getResult(aDialogOutput); + bool bChanged = aDialogOutput.writeDifferenceToModel( getChartModel(), m_xCC, &aDialogInput ); + if( bChanged ) + aUndoGuard.commit(); + } + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +void ChartController::executeDispatch_DeleteLegend() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId( STR_OBJECT_LEGEND )), + m_xUndoManager ); + + LegendHelper::hideLegend(*getChartModel()); + aUndoGuard.commit(); +} + +void ChartController::executeDispatch_InsertLegend() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_LEGEND )), + m_xUndoManager ); + + LegendHelper::showLegend(*getChartModel(), m_xCC); + aUndoGuard.commit(); +} + +void ChartController::executeDispatch_OpenLegendDialog() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_LEGEND )), + m_xUndoManager ); + + try + { + //prepare and open dialog + SolarMutexGuard aGuard; + SchLegendDlg aDlg(GetChartFrame(), m_xCC); + aDlg.init( getChartModel() ); + if (aDlg.run() == RET_OK) + { + // lock controllers till end of block + ControllerLockGuardUNO aCLGuard( getChartModel() ); + aDlg.writeToModel( getChartModel() ); + aUndoGuard.commit(); + } + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +void ChartController::executeDispatch_InsertMenu_DataLabels() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_DATALABELS )), + m_xUndoManager ); + + //if a series is selected insert labels for that series only: + rtl::Reference< DataSeries > xSeries = + ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getChartModel()); + if( xSeries.is() ) + { + // add labels + DataSeriesHelper::insertDataLabelsToSeriesAndAllPoints( xSeries ); + + OUString aChildParticle( ObjectIdentifier::getStringForType( OBJECTTYPE_DATA_LABELS ) + "=" ); + OUString aObjectCID = ObjectIdentifier::createClassifiedIdentifierForParticles( + ObjectIdentifier::getSeriesParticleFromCID(m_aSelection.getSelectedCID()), aChildParticle ); + + bool bSuccess = ChartController::executeDlg_ObjectProperties_withoutUndoGuard( aObjectCID, true ); + if( bSuccess ) + aUndoGuard.commit(); + return; + } + + try + { + wrapper::AllDataLabelItemConverter aItemConverter( + getChartModel(), + m_pDrawModelWrapper->GetItemPool(), + m_pDrawModelWrapper->getSdrModel(), + getChartModel() ); + SfxItemSet aItemSet = aItemConverter.CreateEmptyItemSet(); + aItemConverter.FillItemSet( aItemSet ); + + //prepare and open dialog + SolarMutexGuard aGuard; + + //get number formatter + NumberFormatterWrapper aNumberFormatterWrapper( getChartModel() ); + SvNumberFormatter* pNumberFormatter = aNumberFormatterWrapper.getSvNumberFormatter(); + + DataLabelsDialog aDlg(GetChartFrame(), aItemSet, pNumberFormatter); + + if (aDlg.run() == RET_OK) + { + SfxItemSet aOutItemSet = aItemConverter.CreateEmptyItemSet(); + aDlg.FillItemSet(aOutItemSet); + // lock controllers till end of block + ControllerLockGuardUNO aCLGuard( getChartModel() ); + bool bChanged = aItemConverter.ApplyItemSet( aOutItemSet );//model should be changed now + if( bChanged ) + aUndoGuard.commit(); + } + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +void ChartController::executeDispatch_InsertMeanValue() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_AVERAGE_LINE )), + m_xUndoManager ); + lcl_InsertMeanValueLine( ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), + getChartModel() ) ); + aUndoGuard.commit(); +} + +void ChartController::executeDispatch_InsertMenu_MeanValues() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_AVERAGE_LINE )), + m_xUndoManager ); + + rtl::Reference< DataSeries > xSeries = + ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xSeries.is() ) + { + //if a series is selected insert mean value only for that series: + lcl_InsertMeanValueLine( xSeries ); + } + else if (rtl::Reference<Diagram> xDiagram = getFirstDiagram()) + { + std::vector< rtl::Reference< DataSeries > > aSeries = + xDiagram->getDataSeries(); + + for( const auto& xSrs : aSeries ) + lcl_InsertMeanValueLine( xSrs ); + } + aUndoGuard.commit(); +} + +void ChartController::executeDispatch_InsertMenu_Trendlines() +{ + OUString aCID = m_aSelection.getSelectedCID(); + + rtl::Reference< DataSeries > xSeries = + ObjectIdentifier::getDataSeriesForCID( aCID, getChartModel() ); + + if( !xSeries.is() ) + return; + + executeDispatch_InsertTrendline(); +} + +void ChartController::executeDispatch_InsertTrendline() +{ + rtl::Reference< DataSeries > xRegressionCurveContainer = + ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getChartModel()); + + if( !xRegressionCurveContainer.is() ) + return; + + UndoLiveUpdateGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_CURVE )), + m_xUndoManager ); + + rtl::Reference< RegressionCurveModel > xCurve = + RegressionCurveHelper::addRegressionCurve( + SvxChartRegress::Linear, + xRegressionCurveContainer ); + + if( !xCurve.is()) + return; + + wrapper::RegressionCurveItemConverter aItemConverter( + xCurve, xRegressionCurveContainer, m_pDrawModelWrapper->getSdrModel().GetItemPool(), + m_pDrawModelWrapper->getSdrModel(), + getChartModel() ); + + // open dialog + SfxItemSet aItemSet = aItemConverter.CreateEmptyItemSet(); + aItemConverter.FillItemSet( aItemSet ); + ObjectPropertiesDialogParameter aDialogParameter( + ObjectIdentifier::createDataCurveCID( + ObjectIdentifier::getSeriesParticleFromCID( m_aSelection.getSelectedCID()), + RegressionCurveHelper::getRegressionCurveIndex( xRegressionCurveContainer, xCurve ), false )); + aDialogParameter.init( getChartModel() ); + ViewElementListProvider aViewElementListProvider( m_pDrawModelWrapper.get()); + SolarMutexGuard aGuard; + SchAttribTabDlg aDialog( + GetChartFrame(), &aItemSet, &aDialogParameter, + &aViewElementListProvider, + getChartModel() ); + + // note: when a user pressed "OK" but didn't change any settings in the + // dialog, the SfxTabDialog returns "Cancel" + if( aDialog.run() == RET_OK || aDialog.DialogWasClosedWithOK()) + { + const SfxItemSet* pOutItemSet = aDialog.GetOutputItemSet(); + if( pOutItemSet ) + { + ControllerLockGuardUNO aCLGuard( getChartModel() ); + aItemConverter.ApplyItemSet( *pOutItemSet ); + } + aUndoGuard.commit(); + } +} + +void ChartController::executeDispatch_InsertErrorBars( bool bYError ) +{ + ObjectType objType = bYError ? OBJECTTYPE_DATA_ERRORS_Y : OBJECTTYPE_DATA_ERRORS_X; + + //if a series is selected insert error bars for that series only: + rtl::Reference< DataSeries > xSeries = + ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getChartModel() ); + + if( xSeries.is()) + { + UndoLiveUpdateGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, + SchResId( bYError ? STR_OBJECT_ERROR_BARS_Y : STR_OBJECT_ERROR_BARS_X )), + m_xUndoManager ); + + // add error bars with standard deviation + uno::Reference< beans::XPropertySet > xErrorBarProp( + StatisticsHelper::addErrorBars( xSeries, + css::chart::ErrorBarStyle::STANDARD_DEVIATION, + bYError)); + + // get an appropriate item converter + wrapper::ErrorBarItemConverter aItemConverter( + getChartModel(), xErrorBarProp, m_pDrawModelWrapper->getSdrModel().GetItemPool(), + m_pDrawModelWrapper->getSdrModel(), + getChartModel() ); + + // open dialog + SfxItemSet aItemSet = aItemConverter.CreateEmptyItemSet(); + aItemSet.Put(SfxBoolItem(SCHATTR_STAT_ERRORBAR_TYPE,bYError)); + aItemConverter.FillItemSet( aItemSet ); + ObjectPropertiesDialogParameter aDialogParameter( + ObjectIdentifier::createClassifiedIdentifierWithParent( + objType, u"", m_aSelection.getSelectedCID())); + aDialogParameter.init( getChartModel() ); + ViewElementListProvider aViewElementListProvider( m_pDrawModelWrapper.get()); + SolarMutexGuard aGuard; + SchAttribTabDlg aDlg( + GetChartFrame(), &aItemSet, &aDialogParameter, + &aViewElementListProvider, + getChartModel() ); + aDlg.SetAxisMinorStepWidthForErrorBarDecimals( + InsertErrorBarsDialog::getAxisMinorStepWidthForErrorBarDecimals( getChartModel(), + m_xChartView, m_aSelection.getSelectedCID())); + + // note: when a user pressed "OK" but didn't change any settings in the + // dialog, the SfxTabDialog returns "Cancel" + if (aDlg.run() == RET_OK || aDlg.DialogWasClosedWithOK()) + { + const SfxItemSet* pOutItemSet = aDlg.GetOutputItemSet(); + if( pOutItemSet ) + { + ControllerLockGuardUNO aCLGuard( getChartModel() ); + aItemConverter.ApplyItemSet( *pOutItemSet ); + } + aUndoGuard.commit(); + } + } + else + { + //if no series is selected insert error bars for all series + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, + ObjectNameProvider::getName_ObjectForAllSeries( objType ) ), + m_xUndoManager ); + + try + { + wrapper::AllSeriesStatisticsConverter aItemConverter( + getChartModel(), m_pDrawModelWrapper->GetItemPool() ); + SfxItemSet aItemSet = aItemConverter.CreateEmptyItemSet(); + aItemConverter.FillItemSet( aItemSet ); + + //prepare and open dialog + SolarMutexGuard aGuard; + InsertErrorBarsDialog aDlg( + GetChartFrame(), aItemSet, + getChartModel(), + bYError ? ErrorBarResources::ERROR_BAR_Y : ErrorBarResources::ERROR_BAR_X); + + aDlg.SetAxisMinorStepWidthForErrorBarDecimals( + InsertErrorBarsDialog::getAxisMinorStepWidthForErrorBarDecimals( getChartModel(), m_xChartView, u"" ) ); + + if (aDlg.run() == RET_OK) + { + SfxItemSet aOutItemSet = aItemConverter.CreateEmptyItemSet(); + aDlg.FillItemSet( aOutItemSet ); + + // lock controllers till end of block + ControllerLockGuardUNO aCLGuard( getChartModel() ); + bool bChanged = aItemConverter.ApplyItemSet( aOutItemSet );//model should be changed now + if( bChanged ) + aUndoGuard.commit(); + } + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } + } +} + +void ChartController::executeDispatch_InsertTrendlineEquation( bool bInsertR2 ) +{ + uno::Reference< chart2::XRegressionCurve > xRegCurve( + ObjectIdentifier::getObjectPropertySet( m_aSelection.getSelectedCID(), getChartModel() ), uno::UNO_QUERY ); + if( !xRegCurve.is() ) + { + rtl::Reference< DataSeries > xRegCurveCnt = + ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getChartModel() ); + xRegCurve.set( RegressionCurveHelper::getFirstCurveNotMeanValueLine( xRegCurveCnt ) ); + } + if( !xRegCurve.is()) + return; + + uno::Reference< beans::XPropertySet > xEqProp( xRegCurve->getEquationProperties()); + if( xEqProp.is()) + { + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_CURVE_EQUATION )), + m_xUndoManager ); + xEqProp->setPropertyValue( "ShowEquation", uno::Any( true )); + xEqProp->setPropertyValue( "XName", uno::Any( OUString("x") )); + xEqProp->setPropertyValue( "YName", uno::Any( OUString("f(x)") )); + xEqProp->setPropertyValue( "ShowCorrelationCoefficient", uno::Any( bInsertR2 )); + aUndoGuard.commit(); + } +} + +void ChartController::executeDispatch_InsertR2Value() +{ + uno::Reference< beans::XPropertySet > xEqProp = + ObjectIdentifier::getObjectPropertySet( m_aSelection.getSelectedCID(), getChartModel() ); + if( xEqProp.is()) + { + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_CURVE_EQUATION )), + m_xUndoManager ); + xEqProp->setPropertyValue( "ShowCorrelationCoefficient", uno::Any( true )); + aUndoGuard.commit(); + } +} + +void ChartController::executeDispatch_DeleteR2Value() +{ + uno::Reference< beans::XPropertySet > xEqProp = + ObjectIdentifier::getObjectPropertySet( m_aSelection.getSelectedCID(), getChartModel() ); + if( xEqProp.is()) + { + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_CURVE_EQUATION )), + m_xUndoManager ); + xEqProp->setPropertyValue( "ShowCorrelationCoefficient", uno::Any( false )); + aUndoGuard.commit(); + } +} + +void ChartController::executeDispatch_DeleteMeanValue() +{ + rtl::Reference< DataSeries > xRegCurveCnt = + ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xRegCurveCnt.is()) + { + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId( STR_OBJECT_AVERAGE_LINE )), + m_xUndoManager ); + RegressionCurveHelper::removeMeanValueLine( xRegCurveCnt ); + aUndoGuard.commit(); + } +} + +void ChartController::executeDispatch_DeleteTrendline() +{ + rtl::Reference< DataSeries > xRegCurveCnt = + ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xRegCurveCnt.is()) + { + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId( STR_OBJECT_CURVE )), + m_xUndoManager ); + RegressionCurveHelper::removeAllExceptMeanValueLine( xRegCurveCnt ); + aUndoGuard.commit(); + } +} + +void ChartController::executeDispatch_DeleteTrendlineEquation() +{ + rtl::Reference< DataSeries > xRegCurveCnt = + ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xRegCurveCnt.is()) + { + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId( STR_OBJECT_CURVE_EQUATION )), + m_xUndoManager ); + RegressionCurveHelper::removeEquations( xRegCurveCnt ); + aUndoGuard.commit(); + } +} + +void ChartController::executeDispatch_DeleteErrorBars( bool bYError ) +{ + rtl::Reference< DataSeries > xDataSeries = + ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xDataSeries.is()) + { + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId( STR_OBJECT_CURVE )), + m_xUndoManager ); + StatisticsHelper::removeErrorBars( xDataSeries, bYError ); + aUndoGuard.commit(); + } +} + +void ChartController::executeDispatch_InsertDataLabels() +{ + rtl::Reference< DataSeries > xSeries = + ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xSeries.is() ) + { + UndoGuard aUndoGuard( ActionDescriptionProvider::createDescription( ActionDescriptionProvider::ActionType::Insert, + SchResId( STR_OBJECT_DATALABELS )), + m_xUndoManager ); + DataSeriesHelper::insertDataLabelsToSeriesAndAllPoints( xSeries ); + aUndoGuard.commit(); + } +} + +void ChartController::executeDispatch_InsertDataLabel() +{ + UndoGuard aUndoGuard( ActionDescriptionProvider::createDescription( ActionDescriptionProvider::ActionType::Insert, + SchResId( STR_OBJECT_LABEL )), + m_xUndoManager ); + DataSeriesHelper::insertDataLabelToPoint( ObjectIdentifier::getObjectPropertySet( m_aSelection.getSelectedCID(), getChartModel() ) ); + aUndoGuard.commit(); +} + +void ChartController::executeDispatch_DeleteDataLabels() +{ + rtl::Reference< DataSeries > xSeries = + ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xSeries.is() ) + { + UndoGuard aUndoGuard( ActionDescriptionProvider::createDescription( ActionDescriptionProvider::ActionType::Delete, + SchResId( STR_OBJECT_DATALABELS )), + m_xUndoManager ); + DataSeriesHelper::deleteDataLabelsFromSeriesAndAllPoints( xSeries ); + aUndoGuard.commit(); + } +} + +void ChartController::executeDispatch_DeleteDataLabel() +{ + UndoGuard aUndoGuard( ActionDescriptionProvider::createDescription( ActionDescriptionProvider::ActionType::Delete, + SchResId( STR_OBJECT_LABEL )), + m_xUndoManager ); + DataSeriesHelper::deleteDataLabelsFromPoint( ObjectIdentifier::getObjectPropertySet( m_aSelection.getSelectedCID(), getChartModel() ) ); + aUndoGuard.commit(); +} + +void ChartController::executeDispatch_ResetAllDataPoints() +{ + UndoGuard aUndoGuard( ActionDescriptionProvider::createDescription( ActionDescriptionProvider::ActionType::Format, + SchResId( STR_OBJECT_DATAPOINTS )), + m_xUndoManager ); + rtl::Reference< DataSeries > xSeries = ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xSeries.is() ) + xSeries->resetAllDataPoints(); + aUndoGuard.commit(); +} +void ChartController::executeDispatch_ResetDataPoint() +{ + UndoGuard aUndoGuard( ActionDescriptionProvider::createDescription( ActionDescriptionProvider::ActionType::Format, + SchResId( STR_OBJECT_DATAPOINT )), + m_xUndoManager ); + rtl::Reference< DataSeries > xSeries = ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xSeries.is() ) + { + sal_Int32 nPointIndex = ObjectIdentifier::getIndexFromParticleOrCID( m_aSelection.getSelectedCID() ); + xSeries->resetDataPoint( nPointIndex ); + } + aUndoGuard.commit(); +} + +void ChartController::executeDispatch_InsertAxisTitle() +{ + try + { + rtl::Reference< Title > xTitle; + { + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_TITLE )), + m_xUndoManager ); + + rtl::Reference< Axis > xAxis = ObjectIdentifier::getAxisForCID( m_aSelection.getSelectedCID(), getChartModel() ); + sal_Int32 nDimensionIndex = -1; + sal_Int32 nCooSysIndex = -1; + sal_Int32 nAxisIndex = -1; + AxisHelper::getIndicesForAxis( xAxis, getFirstDiagram(), nCooSysIndex, nDimensionIndex, nAxisIndex ); + + TitleHelper::eTitleType eTitleType = TitleHelper::X_AXIS_TITLE; + if( nDimensionIndex==0 ) + eTitleType = nAxisIndex==0 ? TitleHelper::X_AXIS_TITLE : TitleHelper::SECONDARY_X_AXIS_TITLE; + else if( nDimensionIndex==1 ) + eTitleType = nAxisIndex==0 ? TitleHelper::Y_AXIS_TITLE : TitleHelper::SECONDARY_Y_AXIS_TITLE; + else + eTitleType = TitleHelper::Z_AXIS_TITLE; + + std::unique_ptr< ReferenceSizeProvider > apRefSizeProvider( impl_createReferenceSizeProvider()); + xTitle = TitleHelper::createTitle( eTitleType, ObjectNameProvider::getTitleNameByType(eTitleType), getChartModel(), m_xCC, apRefSizeProvider.get() ); + aUndoGuard.commit(); + } + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +void ChartController::executeDispatch_InsertAxis() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_AXIS )), + m_xUndoManager ); + + try + { + rtl::Reference< Axis > xAxis = ObjectIdentifier::getAxisForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xAxis.is() ) + { + AxisHelper::makeAxisVisible( xAxis ); + aUndoGuard.commit(); + } + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +void ChartController::executeDispatch_DeleteAxis() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId( STR_OBJECT_AXIS )), + m_xUndoManager ); + + try + { + rtl::Reference< Axis > xAxis = ObjectIdentifier::getAxisForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xAxis.is() ) + { + AxisHelper::makeAxisInvisible( xAxis ); + aUndoGuard.commit(); + } + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +void ChartController::executeDispatch_InsertMajorGrid() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_GRID )), + m_xUndoManager ); + + try + { + rtl::Reference< Axis > xAxis = ObjectIdentifier::getAxisForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xAxis.is() ) + { + AxisHelper::makeGridVisible( xAxis->getGridProperties2() ); + aUndoGuard.commit(); + } + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +void ChartController::executeDispatch_DeleteMajorGrid() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId( STR_OBJECT_GRID )), + m_xUndoManager ); + + try + { + rtl::Reference< Axis > xAxis = ObjectIdentifier::getAxisForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xAxis.is() ) + { + AxisHelper::makeGridInvisible( xAxis->getGridProperties2() ); + aUndoGuard.commit(); + } + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +void ChartController::executeDispatch_InsertMinorGrid() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Insert, SchResId( STR_OBJECT_GRID )), + m_xUndoManager ); + + try + { + rtl::Reference< Axis > xAxis = ObjectIdentifier::getAxisForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xAxis.is() ) + { + std::vector< rtl::Reference< ::chart::GridProperties > > aSubGrids( xAxis->getSubGridProperties2() ); + for( rtl::Reference< GridProperties > const & props : aSubGrids) + AxisHelper::makeGridVisible( props ); + aUndoGuard.commit(); + } + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +void ChartController::executeDispatch_DeleteMinorGrid() +{ + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId(STR_OBJECT_GRID)), + m_xUndoManager ); + + try + { + rtl::Reference< Axis > xAxis = ObjectIdentifier::getAxisForCID( m_aSelection.getSelectedCID(), getChartModel() ); + if( xAxis.is() ) + { + std::vector< rtl::Reference< ::chart::GridProperties > > aSubGrids( xAxis->getSubGridProperties2() ); + for( rtl::Reference< ::chart::GridProperties > const & props : aSubGrids) + AxisHelper::makeGridInvisible( props ); + aUndoGuard.commit(); + } + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +} //namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartController_Position.cxx b/chart2/source/controller/main/ChartController_Position.cxx new file mode 100644 index 0000000000..9e7194eca8 --- /dev/null +++ b/chart2/source/controller/main/ChartController_Position.cxx @@ -0,0 +1,202 @@ +/* -*- 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 <DrawViewWrapper.hxx> +#include <PositionAndSizeHelper.hxx> +#include <ChartModel.hxx> +#include <ChartModelHelper.hxx> +#include <ChartView.hxx> +#include "UndoGuard.hxx" +#include <ObjectNameProvider.hxx> +#include <DiagramHelper.hxx> +#include <chartview/ExplicitValueProvider.hxx> +#include <CommonConverters.hxx> +#include <svx/ActionDescriptionProvider.hxx> + +#include <comphelper/servicehelper.hxx> +#include <svx/svxids.hrc> +#include <svx/rectenum.hxx> +#include <svl/intitem.hxx> +#include <svx/svxdlg.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/svapp.hxx> +#include <memory> + +namespace chart +{ +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; + +static void lcl_getPositionAndSizeFromItemSet( const SfxItemSet& rItemSet, awt::Rectangle& rPosAndSize, const awt::Size& rOriginalSize ) +{ + tools::Long nPosX(0); + tools::Long nPosY(0); + tools::Long nSizX(0); + tools::Long nSizY(0); + + RectPoint eRP = RectPoint::LT; + + //read position + if (const SfxInt32Item* pPosXItem = rItemSet.GetItemIfSet(SID_ATTR_TRANSFORM_POS_X)) + nPosX = pPosXItem->GetValue(); + if (const SfxInt32Item* pPosYItem = rItemSet.GetItemIfSet(SID_ATTR_TRANSFORM_POS_Y)) + nPosY = pPosYItem->GetValue(); + //read size + if (const SfxUInt32Item* pWidthItem = rItemSet.GetItemIfSet(SID_ATTR_TRANSFORM_WIDTH)) + nSizX = pWidthItem->GetValue(); + if (const SfxUInt32Item* pHeightItem = rItemSet.GetItemIfSet(SID_ATTR_TRANSFORM_HEIGHT)) + nSizY = pHeightItem->GetValue(); + if (const SfxUInt16Item* pSizeItem = rItemSet.GetItemIfSet(SID_ATTR_TRANSFORM_SIZE_POINT)) + eRP=static_cast<RectPoint>(pSizeItem->GetValue()); + + switch( eRP ) + { + case RectPoint::LT: + break; + case RectPoint::MT: + nPosX += ( rOriginalSize.Width - nSizX ) / 2; + break; + case RectPoint::RT: + nPosX += rOriginalSize.Width - nSizX; + break; + case RectPoint::LM: + nPosY += ( rOriginalSize.Height - nSizY ) / 2; + break; + case RectPoint::MM: + nPosX += ( rOriginalSize.Width - nSizX ) / 2; + nPosY += ( rOriginalSize.Height - nSizY ) / 2; + break; + case RectPoint::RM: + nPosX += rOriginalSize.Width - nSizX; + nPosY += ( rOriginalSize.Height - nSizY ) / 2; + break; + case RectPoint::LB: + nPosY += rOriginalSize.Height - nSizY; + break; + case RectPoint::MB: + nPosX += ( rOriginalSize.Width - nSizX ) / 2; + nPosY += rOriginalSize.Height - nSizY; + break; + case RectPoint::RB: + nPosX += rOriginalSize.Width - nSizX; + nPosY += rOriginalSize.Height - nSizY; + break; + default: + break; + } + + rPosAndSize = awt::Rectangle(nPosX,nPosY,nSizX,nSizY); +} + +void ChartController::executeDispatch_PositionAndSize(const ::css::uno::Sequence< ::css::beans::PropertyValue >* pArgs) +{ + const OUString aCID( m_aSelection.getSelectedCID() ); + + if( aCID.isEmpty() ) + return; + + ObjectType eObjectType = ObjectIdentifier::getObjectType( aCID ); + + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::PosSize, + ObjectNameProvider::getName( eObjectType)), + m_xUndoManager ); + + try + { + SfxItemSet aItemSet = m_pDrawViewWrapper->getPositionAndSizeItemSetFromMarkedObject(); + const SfxItemSet* pOutItemSet = nullptr; + if (!pArgs) + { + //prepare and open dialog + SdrView* pSdrView = m_pDrawViewWrapper.get(); + bool bResizePossible = m_aSelection.isResizeableObjectSelected(); + + SolarMutexGuard aGuard; + SvxAbstractDialogFactory * pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSchTransformTabDialog( + GetChartFrame(), &aItemSet, pSdrView, bResizePossible)); + + if( pDlg->Execute() == RET_OK ) + { + pOutItemSet = pDlg->GetOutputItemSet(); + if (pOutItemSet) + aItemSet.Put(*pOutItemSet);//overwrite old values with new values (-> all items are set) + } + } + else + { + const SfxItemPool* pPool = aItemSet.GetPool(); + if (!pPool) + return; + + for (const auto& aProp: *pArgs) + { + sal_Int32 nValue = 0; + aProp.Value >>= nValue; + if (aProp.Name == "TransformPosX") { + aItemSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_POS_X, nValue)); + } + else if (aProp.Name == "TransformPosY") { + aItemSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_POS_Y, nValue)); + } + else if (aProp.Name == "TransformWidth") { + aItemSet.Put(SfxUInt32Item(SID_ATTR_TRANSFORM_WIDTH, static_cast<sal_uInt32>(nValue))); + } + else if (aProp.Name == "TransformHeight") { + aItemSet.Put(SfxUInt32Item(SID_ATTR_TRANSFORM_HEIGHT, static_cast<sal_uInt32>(nValue))); + } + } + } + + if(pOutItemSet || pArgs) + { + awt::Rectangle aOldObjectRect; + if( m_xChartView ) + aOldObjectRect = m_xChartView->getRectangleOfObject(aCID); + + awt::Rectangle aNewObjectRect; + lcl_getPositionAndSizeFromItemSet( aItemSet, aNewObjectRect, ToSize(aOldObjectRect) ); + awt::Size aPageSize( ChartModelHelper::getPageSize( getChartModel() ) ); + awt::Rectangle aPageRect( 0,0,aPageSize.Width,aPageSize.Height ); + + bool bChanged = false; + if ( eObjectType == OBJECTTYPE_LEGEND ) + { + bChanged = DiagramHelper::switchDiagramPositioningToExcludingPositioning(*getChartModel(), false , true); + } + + bool bMoved = PositionAndSizeHelper::moveObject( m_aSelection.getSelectedCID(), getChartModel() + , aNewObjectRect, aOldObjectRect, aPageRect ); + if( bMoved || bChanged ) + aUndoGuard.commit(); + } + } + catch(const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +} //namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartController_Properties.cxx b/chart2/source/controller/main/ChartController_Properties.cxx new file mode 100644 index 0000000000..5f9e9a7d95 --- /dev/null +++ b/chart2/source/controller/main/ChartController_Properties.cxx @@ -0,0 +1,840 @@ +/* -*- 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 <ChartView.hxx> +#include <chartview/DrawModelWrapper.hxx> +#include <chartview/ChartSfxItemIds.hxx> +#include <ObjectIdentifier.hxx> +#include <chartview/ExplicitScaleValues.hxx> +#include <chartview/ExplicitValueProvider.hxx> +#include <dlg_ObjectProperties.hxx> +#include <dlg_View3D.hxx> +#include <dlg_InsertErrorBars.hxx> +#include <ViewElementListProvider.hxx> +#include <DataPointItemConverter.hxx> +#include <TextLabelItemConverter.hxx> +#include <AxisItemConverter.hxx> +#include <MultipleChartConverters.hxx> +#include <TitleItemConverter.hxx> +#include <LegendItemConverter.hxx> +#include <DataTableItemConverter.hxx> +#include <RegressionCurveItemConverter.hxx> +#include <RegressionEquationItemConverter.hxx> +#include <ErrorBarItemConverter.hxx> +#include <ChartModelHelper.hxx> +#include <Axis.hxx> +#include <AxisHelper.hxx> +#include <TitleHelper.hxx> +#include <ChartType.hxx> +#include <ChartTypeHelper.hxx> +#include <ChartModel.hxx> +#include <ColorPerPointHelper.hxx> +#include <DataSeries.hxx> +#include <DataSeriesProperties.hxx> +#include <DiagramHelper.hxx> +#include <Diagram.hxx> +#include <ControllerLockGuard.hxx> +#include "UndoGuard.hxx" +#include <ObjectNameProvider.hxx> +#include <ResId.hxx> +#include <strings.hrc> +#include <ReferenceSizeProvider.hxx> +#include <RegressionCurveHelper.hxx> +#include <RegressionCurveModel.hxx> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <comphelper/servicehelper.hxx> +#include <o3tl/string_view.hxx> + +#include <memory> + +#include <vcl/svapp.hxx> +#include <svx/ActionDescriptionProvider.hxx> +#include <comphelper/diagnose_ex.hxx> + +namespace chart +{ +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using namespace ::chart::DataSeriesProperties; +using ::com::sun::star::uno::Reference; + +namespace +{ + +wrapper::ItemConverter* createItemConverter( + std::u16string_view aObjectCID, const rtl::Reference<::chart::ChartModel>& xChartModel, + const uno::Reference<uno::XComponentContext>& xContext, SdrModel& rDrawModel, + ExplicitValueProvider* pExplicitValueProvider, ReferenceSizeProvider const * pRefSizeProvider ) +{ + wrapper::ItemConverter* pItemConverter=nullptr; + + //get type of selected object + ObjectType eObjectType = ObjectIdentifier::getObjectType( aObjectCID ); + if( eObjectType==OBJECTTYPE_UNKNOWN ) + { + OSL_FAIL("unknown ObjectType"); + return nullptr; + } + + std::u16string_view aParticleID = ObjectIdentifier::getParticleID( aObjectCID ); + bool bAffectsMultipleObjects = aParticleID == u"ALLELEMENTS"; + if( !bAffectsMultipleObjects ) + { + uno::Reference< beans::XPropertySet > xObjectProperties = + ObjectIdentifier::getObjectPropertySet( aObjectCID, xChartModel ); + if(!xObjectProperties.is()) + return nullptr; + //create itemconverter for a single object + switch(eObjectType) + { + case OBJECTTYPE_PAGE: + pItemConverter = new wrapper::GraphicPropertyItemConverter( + xObjectProperties, rDrawModel.GetItemPool(), + rDrawModel, xChartModel, + wrapper::GraphicObjectType::LineAndFillProperties ); + break; + case OBJECTTYPE_TITLE: + { + std::unique_ptr<awt::Size> pRefSize; + if (pRefSizeProvider) + pRefSize.reset(new awt::Size(pRefSizeProvider->getPageSize())); + + pItemConverter = new wrapper::TitleItemConverter( + xObjectProperties, rDrawModel.GetItemPool(), rDrawModel, + xChartModel, + pRefSize.get()); + } + break; + case OBJECTTYPE_LEGEND: + { + std::unique_ptr<awt::Size> pRefSize; + if (pRefSizeProvider) + pRefSize.reset( new awt::Size( pRefSizeProvider->getPageSize())); + + pItemConverter = new wrapper::LegendItemConverter( + xObjectProperties, rDrawModel.GetItemPool(), rDrawModel, + xChartModel, + pRefSize.get()); + } + break; + case OBJECTTYPE_LEGEND_ENTRY: + break; + case OBJECTTYPE_DIAGRAM: + break; + case OBJECTTYPE_DIAGRAM_WALL: + case OBJECTTYPE_DIAGRAM_FLOOR: + pItemConverter = new wrapper::GraphicPropertyItemConverter( + xObjectProperties, rDrawModel.GetItemPool(), + rDrawModel, xChartModel, + wrapper::GraphicObjectType::LineAndFillProperties ); + break; + case OBJECTTYPE_AXIS: + { + std::unique_ptr<awt::Size> pRefSize; + if (pRefSizeProvider) + pRefSize.reset( new awt::Size( pRefSizeProvider->getPageSize())); + + // the second property set contains the property CoordinateOrigin + // nOriginIndex is the index of the corresponding index of the + // origin (x=0, y=1, z=2) + + ExplicitScaleData aExplicitScale; + ExplicitIncrementData aExplicitIncrement; + if( pExplicitValueProvider ) + pExplicitValueProvider->getExplicitValuesForAxis( + dynamic_cast< Axis* >( xObjectProperties.get() ), + aExplicitScale, aExplicitIncrement ); + + pItemConverter = new wrapper::AxisItemConverter( + xObjectProperties, rDrawModel.GetItemPool(), + rDrawModel, + xChartModel, + &aExplicitScale, &aExplicitIncrement, + pRefSize.get() ); + } + break; + case OBJECTTYPE_AXIS_UNITLABEL: + break; + case OBJECTTYPE_DATA_LABELS: + case OBJECTTYPE_DATA_LABEL: + { + std::unique_ptr<awt::Size> pRefSize; + if (pRefSizeProvider) + pRefSize.reset( new awt::Size( pRefSizeProvider->getPageSize())); + + rtl::Reference<DataSeries> xSeries = ObjectIdentifier::getDataSeriesForCID(aObjectCID, xChartModel); + + bool bDataSeries = eObjectType == OBJECTTYPE_DATA_LABELS; + + sal_Int32 nNumberFormat = ExplicitValueProvider::getExplicitNumberFormatKeyForDataLabel( xObjectProperties ); + sal_Int32 nPercentNumberFormat = ExplicitValueProvider::getExplicitPercentageNumberFormatKeyForDataLabel( + xObjectProperties, xChartModel); + + pItemConverter = new wrapper::TextLabelItemConverter( + xChartModel, xObjectProperties, xSeries, + rDrawModel.GetItemPool(), pRefSize.get(), bDataSeries, + nNumberFormat, nPercentNumberFormat); + } + break; + case OBJECTTYPE_DATA_SERIES: + case OBJECTTYPE_DATA_POINT: + { + std::unique_ptr<awt::Size> pRefSize; + if (pRefSizeProvider) + pRefSize.reset( new awt::Size( pRefSizeProvider->getPageSize())); + + wrapper::GraphicObjectType eMapTo = + wrapper::GraphicObjectType::FilledDataPoint; + + rtl::Reference< DataSeries > xSeries = ObjectIdentifier::getDataSeriesForCID( aObjectCID, xChartModel ); + rtl::Reference< ChartType > xChartType = ChartModelHelper::getChartTypeOfSeries( xChartModel, xSeries ); + + rtl::Reference< Diagram > xDiagram = xChartModel->getFirstChartDiagram(); + sal_Int32 nDimensionCount = xDiagram->getDimension(); + if( !ChartTypeHelper::isSupportingAreaProperties( xChartType, nDimensionCount ) ) + eMapTo = wrapper::GraphicObjectType::LineDataPoint; + + bool bDataSeries = eObjectType == OBJECTTYPE_DATA_SERIES; + + //special color for pie chart: + bool bUseSpecialFillColor = false; + sal_Int32 nSpecialFillColor =0; + sal_Int32 nPointIndex = -1; /*-1 for whole series*/ + if(!bDataSeries) + { + nPointIndex = o3tl::toInt32(aParticleID); + bool bVaryColorsByPoint = false; + if( xSeries.is() && + // "VaryColorsByPoint" + (xSeries->getFastPropertyValue(PROP_DATASERIES_VARY_COLORS_BY_POINT) >>= bVaryColorsByPoint) && + bVaryColorsByPoint ) + { + if( !ColorPerPointHelper::hasPointOwnColor( xSeries, nPointIndex, xObjectProperties ) ) + { + bUseSpecialFillColor = true; + OSL_ASSERT( xDiagram.is()); + uno::Reference< XColorScheme > xColorScheme( xDiagram->getDefaultColorScheme() ); + if( xColorScheme.is()) + nSpecialFillColor = xColorScheme->getColorByIndex( nPointIndex ); + } + } + } + sal_Int32 nNumberFormat=ExplicitValueProvider::getExplicitNumberFormatKeyForDataLabel( xObjectProperties ); + sal_Int32 nPercentNumberFormat=ExplicitValueProvider::getExplicitPercentageNumberFormatKeyForDataLabel( + xObjectProperties, xChartModel); + + pItemConverter = new wrapper::DataPointItemConverter( xChartModel, xContext, + xObjectProperties, xSeries, rDrawModel.GetItemPool(), rDrawModel, + xChartModel, + eMapTo, pRefSize.get(), bDataSeries, bUseSpecialFillColor, nSpecialFillColor, true, + nNumberFormat, nPercentNumberFormat, nPointIndex ); + break; + } + case OBJECTTYPE_GRID: + case OBJECTTYPE_SUBGRID: + case OBJECTTYPE_DATA_AVERAGE_LINE: + pItemConverter = new wrapper::GraphicPropertyItemConverter( + xObjectProperties, rDrawModel.GetItemPool(), + rDrawModel, xChartModel, + wrapper::GraphicObjectType::LineProperties ); + break; + + case OBJECTTYPE_DATA_ERRORS_X: + case OBJECTTYPE_DATA_ERRORS_Y: + case OBJECTTYPE_DATA_ERRORS_Z: + pItemConverter = new wrapper::ErrorBarItemConverter( + xChartModel, xObjectProperties, rDrawModel.GetItemPool(), + rDrawModel, xChartModel); + break; + + case OBJECTTYPE_DATA_CURVE: + pItemConverter = new wrapper::RegressionCurveItemConverter( + xObjectProperties, + ObjectIdentifier::getDataSeriesForCID( aObjectCID, xChartModel ), + rDrawModel.GetItemPool(), rDrawModel, + xChartModel); + break; + case OBJECTTYPE_DATA_CURVE_EQUATION: + { + std::unique_ptr<awt::Size> pRefSize; + if (pRefSizeProvider) + pRefSize.reset(new awt::Size(pRefSizeProvider->getPageSize())); + + pItemConverter = new wrapper::RegressionEquationItemConverter( + xObjectProperties, rDrawModel.GetItemPool(), rDrawModel, + xChartModel, + pRefSize.get()); + break; + } + case OBJECTTYPE_DATA_STOCK_RANGE: + break; + case OBJECTTYPE_DATA_STOCK_LOSS: + case OBJECTTYPE_DATA_STOCK_GAIN: + pItemConverter = new wrapper::GraphicPropertyItemConverter( + xObjectProperties, rDrawModel.GetItemPool(), + rDrawModel, xChartModel, + wrapper::GraphicObjectType::LineAndFillProperties ); + break; + case OBJECTTYPE_DATA_TABLE: + { + pItemConverter = new wrapper::DataTableItemConverter( + xObjectProperties, rDrawModel.GetItemPool(), + rDrawModel, xChartModel); + } + break; + default: //OBJECTTYPE_UNKNOWN + break; + } + } + else + { + //create itemconverter for all objects of given type + switch(eObjectType) + { + case OBJECTTYPE_TITLE: + pItemConverter = new wrapper::AllTitleItemConverter( xChartModel, rDrawModel.GetItemPool(), + rDrawModel, xChartModel); + break; + case OBJECTTYPE_AXIS: + { + std::unique_ptr<awt::Size> pRefSize; + if (pRefSizeProvider) + pRefSize.reset( new awt::Size( pRefSizeProvider->getPageSize())); + + pItemConverter = new wrapper::AllAxisItemConverter( + xChartModel, rDrawModel.GetItemPool(), + rDrawModel, pRefSize.get()); + } + break; + case OBJECTTYPE_GRID: + case OBJECTTYPE_SUBGRID: + pItemConverter = new wrapper::AllGridItemConverter( xChartModel, rDrawModel.GetItemPool(), + rDrawModel, xChartModel); + break; + default: //for this type it is not supported to change all elements at once + break; + } + + } + return pItemConverter; +} + +OUString lcl_getTitleCIDForCommand( std::string_view rDispatchCommand, const rtl::Reference<::chart::ChartModel> & xChartModel ) +{ + if( rDispatchCommand == "AllTitles") + return ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_TITLE, u"ALLELEMENTS" ); + + TitleHelper::eTitleType nTitleType( TitleHelper::MAIN_TITLE ); + if( rDispatchCommand == "SubTitle" ) + nTitleType = TitleHelper::SUB_TITLE; + else if( rDispatchCommand == "XTitle" ) + nTitleType = TitleHelper::X_AXIS_TITLE; + else if( rDispatchCommand == "YTitle" ) + nTitleType = TitleHelper::Y_AXIS_TITLE; + else if( rDispatchCommand == "ZTitle" ) + nTitleType = TitleHelper::Z_AXIS_TITLE; + else if( rDispatchCommand == "SecondaryXTitle" ) + nTitleType = TitleHelper::SECONDARY_X_AXIS_TITLE; + else if( rDispatchCommand == "SecondaryYTitle" ) + nTitleType = TitleHelper::SECONDARY_Y_AXIS_TITLE; + + rtl::Reference< Title > xTitle( TitleHelper::getTitle( nTitleType, xChartModel ) ); + return ObjectIdentifier::createClassifiedIdentifierForObject( xTitle, xChartModel ); +} + +OUString lcl_getAxisCIDForCommand( std::string_view rDispatchCommand, const rtl::Reference<::chart::ChartModel>& xChartModel ) +{ + if( rDispatchCommand == "DiagramAxisAll") + return ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_AXIS, u"ALLELEMENTS" ); + + sal_Int32 nDimensionIndex=0; + bool bMainAxis=true; + if( rDispatchCommand == "DiagramAxisX") + { + nDimensionIndex=0; bMainAxis=true; + } + else if( rDispatchCommand == "DiagramAxisY") + { + nDimensionIndex=1; bMainAxis=true; + } + else if( rDispatchCommand == "DiagramAxisZ") + { + nDimensionIndex=2; bMainAxis=true; + } + else if( rDispatchCommand == "DiagramAxisA") + { + nDimensionIndex=0; bMainAxis=false; + } + else if( rDispatchCommand == "DiagramAxisB") + { + nDimensionIndex=1; bMainAxis=false; + } + + rtl::Reference< Diagram > xDiagram = xChartModel->getFirstChartDiagram(); + rtl::Reference< Axis > xAxis = AxisHelper::getAxis( nDimensionIndex, bMainAxis, xDiagram ); + return ObjectIdentifier::createClassifiedIdentifierForObject( xAxis, xChartModel ); +} + +OUString lcl_getGridCIDForCommand( std::string_view rDispatchCommand, const rtl::Reference<::chart::ChartModel>& xChartModel ) +{ + rtl::Reference< Diagram > xDiagram = xChartModel->getFirstChartDiagram(); + + if( rDispatchCommand == "DiagramGridAll") + return ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_GRID, u"ALLELEMENTS" ); + + sal_Int32 nDimensionIndex=0; + bool bMainGrid=true; + + //x and y is swapped in the commands + + if( rDispatchCommand == "DiagramGridYMain") + { + nDimensionIndex=0; bMainGrid=true; + } + else if( rDispatchCommand == "DiagramGridXMain") + { + nDimensionIndex=1; bMainGrid=true; + } + else if( rDispatchCommand == "DiagramGridZMain") + { + nDimensionIndex=2; bMainGrid=true; + } + else if( rDispatchCommand == "DiagramGridYHelp") + { + nDimensionIndex=0; bMainGrid=false; + } + else if( rDispatchCommand == "DiagramGridXHelp") + { + nDimensionIndex=1; bMainGrid=false; + } + else if( rDispatchCommand == "DiagramGridZHelp") + { + nDimensionIndex=2; bMainGrid=false; + } + + rtl::Reference< Axis > xAxis = AxisHelper::getAxis( nDimensionIndex, true/*bMainAxis*/, xDiagram ); + + sal_Int32 nSubGridIndex= bMainGrid ? -1 : 0; + OUString aCID( ObjectIdentifier::createClassifiedIdentifierForGrid( xAxis, xChartModel, nSubGridIndex ) ); + return aCID; +} + +OUString lcl_getErrorCIDForCommand( const ObjectType eDispatchType, const ObjectType &eSelectedType, const OUString &rSelectedCID) +{ + if( eSelectedType == eDispatchType ) + return rSelectedCID; + + return ObjectIdentifier::createClassifiedIdentifierWithParent( eDispatchType, u"", rSelectedCID ); +} + +OUString lcl_getObjectCIDForCommand( std::string_view rDispatchCommand, const rtl::Reference<::chart::ChartModel> & xChartDocument, const OUString& rSelectedCID ) +{ + ObjectType eObjectType = OBJECTTYPE_UNKNOWN; + + const ObjectType eSelectedType = ObjectIdentifier::getObjectType( rSelectedCID ); + rtl::Reference< DataSeries > xSeries = ObjectIdentifier::getDataSeriesForCID( rSelectedCID, xChartDocument ); + + //legend + if( rDispatchCommand == "Legend" || rDispatchCommand == "FormatLegend" ) + { + eObjectType = OBJECTTYPE_LEGEND; + //@todo set particular aParticleID if we have more than one legend + } + //wall floor area + else if( rDispatchCommand == "DiagramWall" || rDispatchCommand == "FormatWall" ) + { + //OBJECTTYPE_DIAGRAM; + eObjectType = OBJECTTYPE_DIAGRAM_WALL; + //@todo set particular aParticleID if we have more than one diagram + } + else if( rDispatchCommand == "DiagramFloor" || rDispatchCommand == "FormatFloor" ) + { + eObjectType = OBJECTTYPE_DIAGRAM_FLOOR; + //@todo set particular aParticleID if we have more than one diagram + } + else if( rDispatchCommand == "DiagramArea" || rDispatchCommand == "FormatChartArea" ) + { + eObjectType = OBJECTTYPE_PAGE; + } + //title + else if( rDispatchCommand == "MainTitle" + || rDispatchCommand == "SubTitle" + || rDispatchCommand == "XTitle" + || rDispatchCommand == "YTitle" + || rDispatchCommand == "ZTitle" + || rDispatchCommand == "SecondaryXTitle" + || rDispatchCommand == "SecondaryYTitle" + || rDispatchCommand == "AllTitles" + ) + { + return lcl_getTitleCIDForCommand( rDispatchCommand, xChartDocument ); + } + //axis + else if( rDispatchCommand == "DiagramAxisX" + || rDispatchCommand == "DiagramAxisY" + || rDispatchCommand == "DiagramAxisZ" + || rDispatchCommand == "DiagramAxisA" + || rDispatchCommand == "DiagramAxisB" + || rDispatchCommand == "DiagramAxisAll" + ) + { + return lcl_getAxisCIDForCommand( rDispatchCommand, xChartDocument ); + } + //grid + else if( rDispatchCommand == "DiagramGridYMain" + || rDispatchCommand == "DiagramGridXMain" + || rDispatchCommand == "DiagramGridZMain" + || rDispatchCommand == "DiagramGridYHelp" + || rDispatchCommand == "DiagramGridXHelp" + || rDispatchCommand == "DiagramGridZHelp" + || rDispatchCommand == "DiagramGridAll" + ) + { + return lcl_getGridCIDForCommand( rDispatchCommand, xChartDocument ); + } + //data series + else if( rDispatchCommand == "FormatDataSeries" ) + { + if( eSelectedType == OBJECTTYPE_DATA_SERIES ) + return rSelectedCID; + else + return ObjectIdentifier::createClassifiedIdentifier( + OBJECTTYPE_DATA_SERIES, ObjectIdentifier::getSeriesParticleFromCID( rSelectedCID ) ); + } + //data point + else if( rDispatchCommand == "FormatDataPoint" ) + { + return rSelectedCID; + } + //data labels + else if( rDispatchCommand == "FormatDataLabels" ) + { + if( eSelectedType == OBJECTTYPE_DATA_LABELS ) + return rSelectedCID; + else + return ObjectIdentifier::createClassifiedIdentifierWithParent( + OBJECTTYPE_DATA_LABELS, u"", rSelectedCID ); + } + //data labels + else if( rDispatchCommand == "FormatDataLabel" ) + { + if( eSelectedType == OBJECTTYPE_DATA_LABEL ) + return rSelectedCID; + else + { + sal_Int32 nPointIndex = o3tl::toInt32(ObjectIdentifier::getParticleID( rSelectedCID )); + if( nPointIndex>=0 ) + { + OUString aSeriesParticle = ObjectIdentifier::getSeriesParticleFromCID( rSelectedCID ); + OUString aChildParticle( ObjectIdentifier::getStringForType( OBJECTTYPE_DATA_LABELS ) + "=" ); + OUString aLabelsCID = ObjectIdentifier::createClassifiedIdentifierForParticles( aSeriesParticle, aChildParticle ); + OUString aLabelCID_Stub = ObjectIdentifier::createClassifiedIdentifierWithParent( + OBJECTTYPE_DATA_LABEL, u"", aLabelsCID ); + + return ObjectIdentifier::createPointCID( aLabelCID_Stub, nPointIndex ); + } + } + } + //mean value line + else if( rDispatchCommand == "FormatMeanValue" ) + { + if( eSelectedType == OBJECTTYPE_DATA_AVERAGE_LINE ) + return rSelectedCID; + else + return ObjectIdentifier::createDataCurveCID( + ObjectIdentifier::getSeriesParticleFromCID( rSelectedCID ), + RegressionCurveHelper::getRegressionCurveIndex( xSeries, + RegressionCurveHelper::getMeanValueLine( xSeries ) ), true ); + } + //trend line + else if( rDispatchCommand == "FormatTrendline" ) + { + if( eSelectedType == OBJECTTYPE_DATA_CURVE ) + return rSelectedCID; + else + return ObjectIdentifier::createDataCurveCID( + ObjectIdentifier::getSeriesParticleFromCID( rSelectedCID ), + RegressionCurveHelper::getRegressionCurveIndex( xSeries, + RegressionCurveHelper::getFirstCurveNotMeanValueLine( xSeries ) ), false ); + } + //trend line equation + else if( rDispatchCommand == "FormatTrendlineEquation" ) + { + if( eSelectedType == OBJECTTYPE_DATA_CURVE_EQUATION ) + return rSelectedCID; + else + return ObjectIdentifier::createDataCurveEquationCID( + ObjectIdentifier::getSeriesParticleFromCID( rSelectedCID ), + RegressionCurveHelper::getRegressionCurveIndex( xSeries, + RegressionCurveHelper::getFirstCurveNotMeanValueLine( xSeries ) ) ); + } + // y error bars + else if( rDispatchCommand == "FormatXErrorBars" ) + { + return lcl_getErrorCIDForCommand(OBJECTTYPE_DATA_ERRORS_X, eSelectedType, rSelectedCID ); + } + // y error bars + else if( rDispatchCommand == "FormatYErrorBars" ) + { + return lcl_getErrorCIDForCommand(OBJECTTYPE_DATA_ERRORS_Y, eSelectedType, rSelectedCID ); + } + // axis + else if( rDispatchCommand == "FormatAxis" ) + { + if( eSelectedType == OBJECTTYPE_AXIS ) + return rSelectedCID; + else + { + rtl::Reference< Axis > xAxis = ObjectIdentifier::getAxisForCID( rSelectedCID, xChartDocument ); + return ObjectIdentifier::createClassifiedIdentifierForObject( xAxis , xChartDocument ); + } + } + // major grid + else if( rDispatchCommand == "FormatMajorGrid" ) + { + if( eSelectedType == OBJECTTYPE_GRID ) + return rSelectedCID; + else + { + rtl::Reference< Axis > xAxis = ObjectIdentifier::getAxisForCID( rSelectedCID, xChartDocument ); + return ObjectIdentifier::createClassifiedIdentifierForGrid( xAxis, xChartDocument ); + } + + } + // minor grid + else if( rDispatchCommand == "FormatMinorGrid" ) + { + if( eSelectedType == OBJECTTYPE_SUBGRID ) + return rSelectedCID; + else + { + rtl::Reference< Axis > xAxis = ObjectIdentifier::getAxisForCID( rSelectedCID, xChartDocument ); + return ObjectIdentifier::createClassifiedIdentifierForGrid( xAxis, xChartDocument, 0 /*sub grid index*/ ); + } + } + // title + else if( rDispatchCommand == "FormatTitle" ) + { + if( eSelectedType == OBJECTTYPE_TITLE ) + return rSelectedCID; + } + // stock loss + else if( rDispatchCommand == "FormatStockLoss" ) + { + if( eSelectedType == OBJECTTYPE_DATA_STOCK_LOSS ) + return rSelectedCID; + else + return ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DATA_STOCK_LOSS, u""); + } + // stock gain + else if( rDispatchCommand == "FormatStockGain" ) + { + if( eSelectedType == OBJECTTYPE_DATA_STOCK_GAIN ) + return rSelectedCID; + else + return ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DATA_STOCK_GAIN, u"" ); + } + + return ObjectIdentifier::createClassifiedIdentifier( + eObjectType, + u"" ); // aParticleID +} + +} +// anonymous namespace + +void ChartController::executeDispatch_FormatObject(std::u16string_view rDispatchCommand) +{ + rtl::Reference<::chart::ChartModel> xChartDocument( getChartModel() ); + OString aCommand( OUStringToOString( rDispatchCommand, RTL_TEXTENCODING_ASCII_US ) ); + OUString rObjectCID = lcl_getObjectCIDForCommand( aCommand, xChartDocument, m_aSelection.getSelectedCID() ); + executeDlg_ObjectProperties( rObjectCID ); +} + +void ChartController::executeDispatch_ObjectProperties() +{ + executeDlg_ObjectProperties( m_aSelection.getSelectedCID() ); +} + +namespace +{ + +OUString lcl_getFormatCIDforSelectedCID( const OUString& rSelectedCID ) +{ + OUString aFormatCID(rSelectedCID); + + //get type of selected object + ObjectType eObjectType = ObjectIdentifier::getObjectType( aFormatCID ); + + // some legend entries are handled as if they were data series + if( eObjectType==OBJECTTYPE_LEGEND_ENTRY ) + { + std::u16string_view aParentParticle( ObjectIdentifier::getFullParentParticle( rSelectedCID ) ); + aFormatCID = ObjectIdentifier::createClassifiedIdentifierForParticle( aParentParticle ); + } + + // treat diagram as wall + if( eObjectType==OBJECTTYPE_DIAGRAM ) + aFormatCID = ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL, u"" ); + + return aFormatCID; +} + +}//end anonymous namespace + +void ChartController::executeDlg_ObjectProperties( const OUString& rSelectedObjectCID ) +{ + OUString aObjectCID = lcl_getFormatCIDforSelectedCID( rSelectedObjectCID ); + + UndoGuard aUndoGuard( ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Format, + ObjectNameProvider::getName( ObjectIdentifier::getObjectType( aObjectCID ))), + m_xUndoManager ); + + bool bSuccess = ChartController::executeDlg_ObjectProperties_withoutUndoGuard( aObjectCID, false ); + if( bSuccess ) + aUndoGuard.commit(); +} + +bool ChartController::executeDlg_ObjectProperties_withoutUndoGuard( + const OUString& rObjectCID, bool bSuccessOnUnchanged ) +{ + //return true if the properties were changed successfully + bool bRet = false; + if( rObjectCID.isEmpty() ) + { + return bRet; + } + try + { + //get type of object + ObjectType eObjectType = ObjectIdentifier::getObjectType( rObjectCID ); + if( eObjectType==OBJECTTYPE_UNKNOWN ) + { + return bRet; + } + if( eObjectType==OBJECTTYPE_DIAGRAM_WALL || eObjectType==OBJECTTYPE_DIAGRAM_FLOOR ) + { + if( !getFirstDiagram()->isSupportingFloorAndWall() ) + return bRet; + } + + //convert properties to ItemSet + + std::unique_ptr<ReferenceSizeProvider> pRefSizeProv(impl_createReferenceSizeProvider()); + + rtl::Reference<::chart::ChartModel> xChartDoc(getChartModel()); + + std::unique_ptr<wrapper::ItemConverter> pItemConverter( + createItemConverter( rObjectCID, xChartDoc, m_xCC, + m_pDrawModelWrapper->getSdrModel(), + m_xChartView.get(), + pRefSizeProv.get())); + + if (!pItemConverter) + return bRet; + + SfxItemSet aItemSet = pItemConverter->CreateEmptyItemSet(); + + if ( eObjectType == OBJECTTYPE_DATA_ERRORS_X || eObjectType == OBJECTTYPE_DATA_ERRORS_Y ) + aItemSet.Put(SfxBoolItem(SCHATTR_STAT_ERRORBAR_TYPE, eObjectType == OBJECTTYPE_DATA_ERRORS_Y )); + + pItemConverter->FillItemSet(aItemSet); + + //prepare dialog + ObjectPropertiesDialogParameter aDialogParameter( rObjectCID ); + aDialogParameter.init(xChartDoc); + ViewElementListProvider aViewElementListProvider( m_pDrawModelWrapper.get() ); + + SolarMutexGuard aGuard; + SchAttribTabDlg aDlg( + GetChartFrame(), &aItemSet, &aDialogParameter, + &aViewElementListProvider, + xChartDoc ); + + if(aDialogParameter.HasSymbolProperties()) + { + uno::Reference< beans::XPropertySet > xObjectProperties = + ObjectIdentifier::getObjectPropertySet( rObjectCID, xChartDoc ); + wrapper::DataPointItemConverter aSymbolItemConverter( xChartDoc, m_xCC + , xObjectProperties, ObjectIdentifier::getDataSeriesForCID( rObjectCID, xChartDoc ) + , m_pDrawModelWrapper->getSdrModel().GetItemPool() + , m_pDrawModelWrapper->getSdrModel() + , xChartDoc + , wrapper::GraphicObjectType::FilledDataPoint ); + + SfxItemSet aSymbolShapeProperties(aSymbolItemConverter.CreateEmptyItemSet() ); + aSymbolItemConverter.FillItemSet( aSymbolShapeProperties ); + + sal_Int32 const nStandardSymbol=0;//@todo get from somewhere + std::optional<Graphic> oAutoSymbolGraphic(std::in_place, aViewElementListProvider.GetSymbolGraphic( nStandardSymbol, &aSymbolShapeProperties ) ); + // note: the dialog takes the ownership of pSymbolShapeProperties and pAutoSymbolGraphic + aDlg.setSymbolInformation( std::move(aSymbolShapeProperties), std::move(oAutoSymbolGraphic) ); + } + if( aDialogParameter.HasStatisticProperties() ) + { + aDlg.SetAxisMinorStepWidthForErrorBarDecimals( + InsertErrorBarsDialog::getAxisMinorStepWidthForErrorBarDecimals( xChartDoc, m_xChartView, rObjectCID ) ); + } + + //open the dialog + if (aDlg.run() == RET_OK || (bSuccessOnUnchanged && aDlg.DialogWasClosedWithOK())) + { + const SfxItemSet* pOutItemSet = aDlg.GetOutputItemSet(); + if(pOutItemSet) + { + ControllerLockGuardUNO aCLGuard(xChartDoc); + (void)pItemConverter->ApplyItemSet(*pOutItemSet); //model should be changed now + bRet = true; + } + } + } + catch( const util::CloseVetoException& ) + { + } + catch( const uno::RuntimeException& ) + { + } + return bRet; +} + +void ChartController::executeDispatch_View3D() +{ + try + { + UndoLiveUpdateGuard aUndoGuard( + SchResId( STR_ACTION_EDIT_3D_VIEW ), + m_xUndoManager ); + + //open dialog + SolarMutexGuard aSolarGuard; + View3DDialog aDlg(GetChartFrame(), getChartModel()); + if (aDlg.run() == RET_OK) + aUndoGuard.commit(); + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +} //namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartController_TextEdit.cxx b/chart2/source/controller/main/ChartController_TextEdit.cxx new file mode 100644 index 0000000000..f2d21779ba --- /dev/null +++ b/chart2/source/controller/main/ChartController_TextEdit.cxx @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_wasm_strip.h> + +#include <ChartController.hxx> + +#include <ResId.hxx> +#include "UndoGuard.hxx" +#include <DrawViewWrapper.hxx> +#include <ChartWindow.hxx> +#include <ChartModel.hxx> +#include <ChartView.hxx> +#include <TitleHelper.hxx> +#include <ObjectIdentifier.hxx> +#include <ControllerLockGuard.hxx> +#if !ENABLE_WASM_STRIP_ACCESSIBILITY +#include <AccessibleTextHelper.hxx> +#endif +#include <strings.hrc> +#include <chartview/DrawModelWrapper.hxx> +#include <osl/diagnose.h> + +#include <svx/svdoutl.hxx> +#include <svx/svxdlg.hxx> +#include <svx/svxids.hrc> +#include <editeng/editids.hrc> +#include <vcl/svapp.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/chart2/XTitle.hpp> +#include <svl/stritem.hxx> +#include <editeng/fontitem.hxx> +#include <memory> + +namespace chart +{ +using namespace ::com::sun::star; + +void ChartController::executeDispatch_EditText( const Point* pMousePixel ) +{ + StartTextEdit( pMousePixel ); +} + +void ChartController::StartTextEdit( const Point* pMousePixel ) +{ + //the first marked object will be edited + + SolarMutexGuard aGuard; + SdrObject* pTextObj = m_pDrawViewWrapper->getTextEditObject(); + if(!pTextObj) + return; + + OSL_PRECOND(!m_pTextActionUndoGuard, + "ChartController::StartTextEdit: already have a TextUndoGuard!?"); + m_pTextActionUndoGuard.reset( new UndoGuard( + SchResId( STR_ACTION_EDIT_TEXT ), m_xUndoManager ) ); + SdrOutliner* pOutliner = m_pDrawViewWrapper->getOutliner(); + + //#i77362 change notification for changes on additional shapes are missing + if( m_xChartView.is() ) + m_xChartView->setPropertyValue( "SdrViewIsInEditMode", uno::Any(true) ); + + auto pChartWindow(GetChartWindow()); + + bool bEdit = m_pDrawViewWrapper->SdrBeginTextEdit( pTextObj + , m_pDrawViewWrapper->GetPageView() + , pChartWindow + , false //bIsNewObj + , pOutliner + , nullptr //pOutlinerView + , true //bDontDeleteOutliner + , true //bOnlyOneView + ); + if(!bEdit) + return; + + m_pDrawViewWrapper->SetEditMode(); + + // #i12587# support for shapes in chart + if ( pMousePixel ) + { + OutlinerView* pOutlinerView = m_pDrawViewWrapper->GetTextEditOutlinerView(); + if ( pOutlinerView ) + { + MouseEvent aEditEvt( *pMousePixel, 1, MouseEventModifiers::SYNTHETIC, MOUSE_LEFT, 0 ); + pOutlinerView->MouseButtonDown( aEditEvt ); + pOutlinerView->MouseButtonUp( aEditEvt ); + } + } + + if (pChartWindow) + { + //we invalidate the outliner region because the outliner has some + //paint problems (some characters are painted twice a little bit shifted) + pChartWindow->Invalidate( m_pDrawViewWrapper->GetMarkedObjBoundRect() ); + } +} + +bool ChartController::EndTextEdit() +{ + m_pDrawViewWrapper->SdrEndTextEdit(); + + //#i77362 change notification for changes on additional shapes are missing + if( m_xChartView.is() ) + m_xChartView->setPropertyValue( "SdrViewIsInEditMode", uno::Any(false) ); + + SdrObject* pTextObject = m_pDrawViewWrapper->getTextEditObject(); + if(!pTextObject) + return false; + + SdrOutliner* pOutliner = m_pDrawViewWrapper->getOutliner(); + OutlinerParaObject* pParaObj = pTextObject->GetOutlinerParaObject(); + if( !pParaObj || !pOutliner ) + return true; + + pOutliner->SetText( *pParaObj ); + + OUString aString = pOutliner->GetText( + pOutliner->GetParagraph( 0 ), + pOutliner->GetParagraphCount() ); + + OUString aObjectCID = m_aSelection.getSelectedCID(); + if ( !aObjectCID.isEmpty() ) + { + uno::Reference< beans::XPropertySet > xPropSet = + ObjectIdentifier::getObjectPropertySet( aObjectCID, getChartModel() ); + + // lock controllers till end of block + ControllerLockGuardUNO aCLGuard( getChartModel() ); + + Title* pTitle = dynamic_cast<Title*>(xPropSet.get()); + TitleHelper::setCompleteString( aString, pTitle, m_xCC ); + + OSL_ENSURE(m_pTextActionUndoGuard, "ChartController::EndTextEdit: no TextUndoGuard!"); + if (m_pTextActionUndoGuard) + m_pTextActionUndoGuard->commit(); + } + m_pTextActionUndoGuard.reset(); + return true; +} + +void ChartController::executeDispatch_InsertSpecialCharacter() +{ + SolarMutexGuard aGuard; + if( !m_pDrawViewWrapper) + { + OSL_ENSURE( m_pDrawViewWrapper, "No DrawViewWrapper for ChartController" ); + return; + } + if( !m_pDrawViewWrapper->IsTextEdit() ) + StartTextEdit(); + + SvxAbstractDialogFactory * pFact = SvxAbstractDialogFactory::Create(); + + SfxAllItemSet aSet( m_pDrawModelWrapper->GetItemPool() ); + aSet.Put( SfxBoolItem( FN_PARAM_1, false ) ); + + //set fixed current font + aSet.Put( SfxBoolItem( FN_PARAM_2, true ) ); //maybe not necessary in future + + vcl::Font aCurFont = m_pDrawViewWrapper->getOutliner()->GetRefDevice()->GetFont(); + aSet.Put( SvxFontItem( aCurFont.GetFamilyType(), aCurFont.GetFamilyName(), aCurFont.GetStyleName(), aCurFont.GetPitch(), aCurFont.GetCharSet(), SID_ATTR_CHAR_FONT ) ); + + ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateCharMapDialog(GetChartFrame(), aSet, nullptr)); + if( pDlg->Execute() != RET_OK ) + return; + + const SfxItemSet* pSet = pDlg->GetOutputItemSet(); + OUString aString; + if (pSet) + if (const SfxStringItem* pCharMapItem = pSet->GetItemIfSet(SID_CHARMAP)) + aString = pCharMapItem->GetValue(); + + OutlinerView* pOutlinerView = m_pDrawViewWrapper->GetTextEditOutlinerView(); + SdrOutliner* pOutliner = m_pDrawViewWrapper->getOutliner(); + + if(!pOutliner || !pOutlinerView) + return; + + // insert string to outliner + + // prevent flicker + pOutlinerView->HideCursor(); + pOutliner->SetUpdateLayout(false); + + // delete current selection by inserting empty String, so current + // attributes become unique (sel. has to be erased anyway) + pOutlinerView->InsertText(OUString()); + + pOutlinerView->InsertText(aString, true); + + ESelection aSel = pOutlinerView->GetSelection(); + aSel.nStartPara = aSel.nEndPara; + aSel.nStartPos = aSel.nEndPos; + pOutlinerView->SetSelection(aSel); + + // show changes + pOutliner->SetUpdateLayout(true); + pOutlinerView->ShowCursor(); +} + +rtl::Reference< ::chart::AccessibleTextHelper > + ChartController::createAccessibleTextContext() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + return new AccessibleTextHelper( m_pDrawViewWrapper.get() ); +#else + return {}; +#endif +} + +} //namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartController_Tools.cxx b/chart2/source/controller/main/ChartController_Tools.cxx new file mode 100644 index 0000000000..48dbfaf7f6 --- /dev/null +++ b/chart2/source/controller/main/ChartController_Tools.cxx @@ -0,0 +1,1127 @@ +/* -*- 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 <ChartWindow.hxx> +#include <ChartModel.hxx> +#include <ChartModelHelper.hxx> +#include <ChartType.hxx> +#include <TitleHelper.hxx> +#include <ThreeDHelper.hxx> +#include <DataSeries.hxx> +#include <DataSeriesHelper.hxx> +#include "UndoGuard.hxx" +#include <ControllerLockGuard.hxx> +#include <ResId.hxx> +#include <strings.hrc> +#include <ObjectIdentifier.hxx> +#include <ReferenceSizeProvider.hxx> +#include <chartview/DrawModelWrapper.hxx> +#include "ChartTransferable.hxx" +#include <DrawViewWrapper.hxx> +#include <Legend.hxx> +#include <LegendHelper.hxx> +#include <Axis.hxx> +#include <AxisHelper.hxx> +#include <RegressionCurveModel.hxx> +#include <RegressionCurveHelper.hxx> +#include "ShapeController.hxx" +#include <DiagramHelper.hxx> +#include <Diagram.hxx> +#include <ObjectNameProvider.hxx> +#include <unonames.hxx> + +#include <com/sun/star/awt/Gradient.hpp> +#include <com/sun/star/chart2/DataPointLabel.hpp> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/drawing/TextVerticalAdjust.hpp> +#include <com/sun/star/drawing/TextHorizontalAdjust.hpp> +#include <com/sun/star/chart/ErrorBarStyle.hpp> +#include <com/sun/star/chart2/XRegressionCurveContainer.hpp> + +#include <docmodel/uno/UnoGradientTools.hxx> +#include <editeng/editview.hxx> +#include <editeng/outliner.hxx> +#include <svx/ActionDescriptionProvider.hxx> +#include <vcl/transfer.hxx> +#include <sot/storage.hxx> +#include <vcl/graph.hxx> +#include <vcl/TypeSerializer.hxx> +#include <svx/unomodel.hxx> +#include <svx/svdmodel.hxx> +#include <unotools/streamwrap.hxx> +#include <vcl/svapp.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <svx/svditer.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdundo.hxx> +#include <svx/unoapi.hxx> +#include <svx/unopage.hxx> +#include <svx/unoshape.hxx> +#include <PropertyHelper.hxx> + +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/UnitConversion.hxx> + +#include <memory> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace chart +{ + +namespace +{ + +bool lcl_deleteDataSeries( + std::u16string_view rCID, + const rtl::Reference<::chart::ChartModel> & xModel, + const Reference< document::XUndoManager > & xUndoManager ) +{ + bool bResult = false; + rtl::Reference< DataSeries > xSeries = ObjectIdentifier::getDataSeriesForCID( rCID, xModel ); + if( xSeries.is() && xModel.is()) + { + rtl::Reference< ::chart::ChartType > xChartType = + DataSeriesHelper::getChartTypeOfSeries( xSeries, xModel->getFirstChartDiagram()); + if( xChartType.is()) + { + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId( STR_OBJECT_DATASERIES )), + xUndoManager ); + + rtl::Reference< Diagram > xDiagram = xModel->getFirstChartDiagram(); + rtl::Reference< Axis > xAxis = xDiagram->getAttachedAxis( xSeries ); + + DataSeriesHelper::deleteSeries( xSeries, xChartType ); + + AxisHelper::hideAxisIfNoDataIsAttached( xAxis, xDiagram ); + + bResult = true; + aUndoGuard.commit(); + } + } + return bResult; +} + +bool lcl_deleteDataCurve( + std::u16string_view rCID, + const rtl::Reference<::chart::ChartModel> & xModel, + const Reference< document::XUndoManager > & xUndoManager ) +{ + bool bResult = false; + + uno::Reference< beans::XPropertySet > xProperties( + ObjectIdentifier::getObjectPropertySet( rCID, xModel)); + + uno::Reference< chart2::XRegressionCurve > xRegressionCurve( xProperties, uno::UNO_QUERY ); + + if( xRegressionCurve.is()) + { + uno::Reference< chart2::XRegressionCurveContainer > xRegressionCurveContainer( + ObjectIdentifier::getObjectPropertySet( + ObjectIdentifier::getFullParentParticle( rCID ), xModel), uno::UNO_QUERY ); + + if( xRegressionCurveContainer.is()) + { + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId( STR_OBJECT_CURVE )), + xUndoManager ); + + xRegressionCurveContainer->removeRegressionCurve( xRegressionCurve ); + + bResult = true; + aUndoGuard.commit(); + } + } + return bResult; +} + +} // anonymous namespace + +std::unique_ptr<ReferenceSizeProvider> ChartController::impl_createReferenceSizeProvider() +{ + awt::Size aPageSize( ChartModelHelper::getPageSize( getChartModel() ) ); + + return std::make_unique<ReferenceSizeProvider>(aPageSize, getChartModel()); +} + +void ChartController::impl_adaptDataSeriesAutoResize() +{ + std::unique_ptr<ReferenceSizeProvider> pRefSizeProvider(impl_createReferenceSizeProvider()); + if (pRefSizeProvider) + pRefSizeProvider->setValuesAtAllDataSeries(); +} + +void ChartController::executeDispatch_NewArrangement() +{ + // remove manual positions at titles, legend and the diagram, remove manual + // size at the diagram + + try + { + rtl::Reference<::chart::ChartModel> xModel( getChartModel() ); + rtl::Reference< Diagram > xDiagram = xModel->getFirstChartDiagram(); + if( xDiagram.is()) + { + UndoGuard aUndoGuard( + SchResId( STR_ACTION_REARRANGE_CHART ), + m_xUndoManager ); + ControllerLockGuardUNO aCtlLockGuard( xModel ); + + // diagram + xDiagram->setPropertyToDefault( "RelativeSize"); + xDiagram->setPropertyToDefault( "RelativePosition"); + xDiagram->setPropertyToDefault( "PosSizeExcludeAxes"); + + // 3d rotation + xDiagram->set3DSettingsToDefault(); + + // legend + rtl::Reference< Legend > xLegend = xDiagram->getLegend2(); + if( xLegend.is()) + { + xLegend->setPropertyToDefault( "RelativePosition"); + xLegend->setPropertyToDefault( "RelativeSize"); + xLegend->setPropertyToDefault( "AnchorPosition"); + } + + // titles + for( sal_Int32 eType = TitleHelper::TITLE_BEGIN; + eType < TitleHelper::NORMAL_TITLE_END; + ++eType ) + { + rtl::Reference< Title > xTitleState = + TitleHelper::getTitle( + static_cast< TitleHelper::eTitleType >( eType ), xModel ); + if( xTitleState.is()) + xTitleState->setPropertyToDefault( "RelativePosition"); + } + + // regression curve equations + std::vector< rtl::Reference< RegressionCurveModel > > aRegressionCurves = + xDiagram->getAllRegressionCurvesNotMeanValueLine(); + + // reset equation position + for( const auto& xCurve : aRegressionCurves ) + RegressionCurveHelper::resetEquationPosition( xCurve ); + + aUndoGuard.commit(); + } + } + catch( const uno::RuntimeException & ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } +} + +void ChartController::executeDispatch_ScaleText() +{ + SolarMutexGuard aSolarGuard; + UndoGuard aUndoGuard( + SchResId( STR_ACTION_SCALE_TEXT ), + m_xUndoManager ); + ControllerLockGuardUNO aCtlLockGuard( getChartModel() ); + + std::unique_ptr<ReferenceSizeProvider> pRefSizeProv(impl_createReferenceSizeProvider()); + OSL_ASSERT(pRefSizeProv); + if (pRefSizeProv) + pRefSizeProv->toggleAutoResizeState(); + + aUndoGuard.commit(); +} + +void ChartController::executeDispatch_Paste() +{ + SolarMutexGuard aGuard; + auto pChartWindow(GetChartWindow()); + if( !pChartWindow ) + return; + + Graphic aGraphic; + // paste location: center of window + Point aPos = pChartWindow->PixelToLogic( tools::Rectangle(Point{}, pChartWindow->GetSizePixel()).Center()); + + // handle different formats + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pChartWindow )); + if( aDataHelper.GetTransferable().is()) + { + if ( aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ) ) + { + tools::SvRef<SotTempStream> xStm; + if ( aDataHelper.GetSotStorageStream( SotClipboardFormatId::DRAWING, xStm ) ) + { + xStm->Seek( 0 ); + Reference< io::XInputStream > xInputStream( new utl::OInputStreamWrapper( *xStm ) ); + + std::unique_ptr< SdrModel > spModel( + new SdrModel()); + + if ( SvxDrawingLayerImport( spModel.get(), xInputStream ) ) + { + impl_PasteShapes( spModel.get() ); + } + } + } + else if ( aDataHelper.HasFormat( SotClipboardFormatId::SVXB ) ) + { + // graphic exchange format (graphic manager bitmap format?) + tools::SvRef<SotTempStream> xStm; + if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::SVXB, xStm )) + { + TypeSerializer aSerializer(*xStm); + aSerializer.readGraphic(aGraphic); + } + } + else if( aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE )) + { + // meta file + GDIMetaFile aMetafile; + if( aDataHelper.GetGDIMetaFile( SotClipboardFormatId::GDIMETAFILE, aMetafile )) + aGraphic = Graphic( aMetafile ); + } + else if( aDataHelper.HasFormat( SotClipboardFormatId::BITMAP )) + { + // bitmap (non-graphic-manager) + BitmapEx aBmpEx; + if( aDataHelper.GetBitmapEx( SotClipboardFormatId::BITMAP, aBmpEx )) + aGraphic = Graphic( aBmpEx ); + } + else if( aDataHelper.HasFormat( SotClipboardFormatId::STRING )) + { + OUString aString; + if( aDataHelper.GetString( SotClipboardFormatId::STRING, aString ) && m_pDrawModelWrapper ) + { + if( m_pDrawViewWrapper ) + { + OutlinerView* pOutlinerView = m_pDrawViewWrapper->GetTextEditOutlinerView(); + if( pOutlinerView )//in case of edit mode insert into edited string + pOutlinerView->InsertText( aString ); + else + { + impl_PasteStringAsTextShape( aString, awt::Point( 0, 0 ) ); + } + } + } + } + } + + if( aGraphic.GetType() != GraphicType::NONE ) + { + Reference< graphic::XGraphic > xGraphic( aGraphic.GetXGraphic()); + if( xGraphic.is()) + impl_PasteGraphic( xGraphic, aPos ); + } +} + +// note: aPosition is ignored for now. The object is always pasted centered to +// the page +void ChartController::impl_PasteGraphic( + uno::Reference< graphic::XGraphic > const & xGraphic, + const ::Point & /* aPosition */ ) +{ + DBG_TESTSOLARMUTEX(); + // note: the XPropertySet of the model is the old API. Also the property + // "AdditionalShapes" that is used there. + rtl::Reference< ChartModel > xModel = getChartModel(); + DrawModelWrapper * pDrawModelWrapper( GetDrawModelWrapper()); + if( ! (xGraphic.is() && xModel.is())) + return; + rtl::Reference<SvxGraphicObject> xGraphicShape = new SvxGraphicObject(nullptr); + xGraphicShape->setShapeKind(SdrObjKind::Graphic); + + uno::Reference< drawing::XShapes > xPage = pDrawModelWrapper->getMainDrawPage(); + if( xPage.is()) + { + xPage->add( xGraphicShape ); + //need to change the model state manually + xModel->setModified( true ); + //select new shape + m_aSelection.setSelection( xGraphicShape ); + m_aSelection.applySelection( m_pDrawViewWrapper.get() ); + } + xGraphicShape->SvxShape::setPropertyValue( "Graphic", uno::Any( xGraphic )); + + awt::Size aGraphicSize( 1000, 1000 ); + bool bGotGraphicSize = false; + try + { + bGotGraphicSize = xGraphicShape->SvxShape::getPropertyValue( "Size100thMM") >>= aGraphicSize; + } + catch (css::beans::UnknownPropertyException& ) + {} + auto pChartWindow(GetChartWindow()); + // first try size in 100th mm, then pixel size + if( !bGotGraphicSize ) + { + bool bGotSizePixel = false; + try + { + bGotSizePixel = xGraphicShape->SvxShape::getPropertyValue( "SizePixel") >>= aGraphicSize; + } + catch (css::beans::UnknownPropertyException& ) + {} + if ( bGotSizePixel && pChartWindow ) + { + ::Size aVCLSize( pChartWindow->PixelToLogic( Size( aGraphicSize.Width, aGraphicSize.Height ))); + aGraphicSize.Width = aVCLSize.getWidth(); + aGraphicSize.Height = aVCLSize.getHeight(); + } + } + xGraphicShape->setSize( aGraphicSize ); + xGraphicShape->setPosition( awt::Point( 0, 0 ) ); +} + +void ChartController::impl_PasteShapes( SdrModel* pModel ) +{ + DrawModelWrapper* pDrawModelWrapper( GetDrawModelWrapper() ); + if ( !(pDrawModelWrapper && m_pDrawViewWrapper) ) + return; + + Reference< drawing::XDrawPage > xDestPage( pDrawModelWrapper->getMainDrawPage() ); + SdrPage* pDestPage = GetSdrPageFromXDrawPage( xDestPage ); + if ( !pDestPage ) + return; + + Reference< drawing::XShape > xSelShape; + m_pDrawViewWrapper->BegUndo( SvxResId( RID_SVX_3D_UNDO_EXCHANGE_PASTE ) ); + sal_uInt16 nCount = pModel->GetPageCount(); + for ( sal_uInt16 i = 0; i < nCount; ++i ) + { + const SdrPage* pPage = pModel->GetPage( i ); + SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups ); + while ( aIter.IsMore() ) + { + SdrObject* pObj(aIter.Next()); + // Clone to new SdrModel + rtl::Reference<SdrObject> pNewObj; + if (pObj) + pNewObj = pObj->CloneSdrObject(pDrawModelWrapper->getSdrModel()); + + if ( pNewObj ) + { + // set position + Reference< drawing::XShape > xShape( pNewObj->getUnoShape(), uno::UNO_QUERY ); + if ( xShape.is() ) + { + xShape->setPosition( awt::Point( 0, 0 ) ); + } + + pDestPage->InsertObject( pNewObj.get() ); + m_pDrawViewWrapper->AddUndo( std::make_unique<SdrUndoInsertObj>( *pNewObj ) ); + xSelShape = xShape; + } + } + } + + rtl::Reference< ChartModel > xModifiable = getChartModel(); + if ( xModifiable.is() ) + { + xModifiable->setModified( true ); + } + + // select last inserted shape + m_aSelection.setSelection( xSelShape ); + m_aSelection.applySelection( m_pDrawViewWrapper.get() ); + + m_pDrawViewWrapper->EndUndo(); + + impl_switchDiagramPositioningToExcludingPositioning(); +} + +void ChartController::impl_PasteStringAsTextShape( const OUString& rString, const awt::Point& rPosition ) +{ + DrawModelWrapper* pDrawModelWrapper( GetDrawModelWrapper() ); + if ( !(pDrawModelWrapper && m_pDrawViewWrapper) ) + return; + + const Reference< drawing::XDrawPage >& xDrawPage( pDrawModelWrapper->getMainDrawPage() ); + OSL_ASSERT( xDrawPage.is() ); + + if ( !xDrawPage ) + return; + + try + { + rtl::Reference<SvxShapeText> xTextShape = new SvxShapeText(nullptr); + xTextShape->setShapeKind(SdrObjKind::Text); + xDrawPage->add( xTextShape ); + + xTextShape->setString( rString ); + + float fCharHeight = 10.0; + xTextShape->SvxShape::setPropertyValue( "TextAutoGrowHeight", uno::Any( true ) ); + xTextShape->SvxShape::setPropertyValue( "TextAutoGrowWidth", uno::Any( true ) ); + xTextShape->SvxShape::setPropertyValue( "CharHeight", uno::Any( fCharHeight ) ); + xTextShape->SvxShape::setPropertyValue( "CharHeightAsian", uno::Any( fCharHeight ) ); + xTextShape->SvxShape::setPropertyValue( "CharHeightComplex", uno::Any( fCharHeight ) ); + xTextShape->SvxShape::setPropertyValue( "TextVerticalAdjust", uno::Any( drawing::TextVerticalAdjust_CENTER ) ); + xTextShape->SvxShape::setPropertyValue( "TextHorizontalAdjust", uno::Any( drawing::TextHorizontalAdjust_CENTER ) ); + xTextShape->SvxShape::setPropertyValue( "CharFontName", uno::Any( OUString("Albany") ) ); + + xTextShape->setPosition( rPosition ); + + m_aSelection.setSelection( xTextShape ); + m_aSelection.applySelection( m_pDrawViewWrapper.get() ); + + SdrObject* pObj = DrawViewWrapper::getSdrObject( xTextShape ); + if ( pObj ) + { + m_pDrawViewWrapper->BegUndo( SvxResId( RID_SVX_3D_UNDO_EXCHANGE_PASTE ) ); + m_pDrawViewWrapper->AddUndo( std::make_unique<SdrUndoInsertObj>( *pObj ) ); + m_pDrawViewWrapper->EndUndo(); + + impl_switchDiagramPositioningToExcludingPositioning(); + } + } + catch ( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } +} + +void ChartController::executeDispatch_Copy() +{ + SolarMutexGuard aSolarGuard; + if (!m_pDrawViewWrapper) + return; + + OutlinerView* pOutlinerView = m_pDrawViewWrapper->GetTextEditOutlinerView(); + if (pOutlinerView) + pOutlinerView->Copy(); + else + { + SdrObject* pSelectedObj = nullptr; + ObjectIdentifier aSelOID(m_aSelection.getSelectedOID()); + + if (aSelOID.isAutoGeneratedObject()) + pSelectedObj = m_pDrawModelWrapper->getNamedSdrObject( aSelOID.getObjectCID() ); + else if (aSelOID.isAdditionalShape()) + pSelectedObj = DrawViewWrapper::getSdrObject( aSelOID.getAdditionalShape() ); + + if (pSelectedObj) + { + Reference<datatransfer::clipboard::XClipboard> xClipboard(GetChartWindow()->GetClipboard()); + if (xClipboard.is()) + { + Reference< datatransfer::XTransferable > xTransferable( + new ChartTransferable(m_pDrawModelWrapper->getSdrModel(), + pSelectedObj, aSelOID.isAdditionalShape())); + xClipboard->setContents(xTransferable, Reference< datatransfer::clipboard::XClipboardOwner >()); + } + } + } +} + +void ChartController::executeDispatch_Cut() +{ + executeDispatch_Copy(); + executeDispatch_Delete(); +} + +bool ChartController::isObjectDeleteable( const uno::Any& rSelection ) +{ + ObjectIdentifier aSelOID( rSelection ); + if ( aSelOID.isAutoGeneratedObject() ) + { + const OUString& aSelObjCID( aSelOID.getObjectCID() ); + ObjectType aObjectType(ObjectIdentifier::getObjectType( aSelObjCID )); + + switch(aObjectType) + { + case OBJECTTYPE_TITLE: + case OBJECTTYPE_LEGEND: + case OBJECTTYPE_DATA_SERIES: + case OBJECTTYPE_LEGEND_ENTRY: + case OBJECTTYPE_DATA_CURVE_EQUATION: + case OBJECTTYPE_DATA_CURVE: + case OBJECTTYPE_DATA_AVERAGE_LINE: + case OBJECTTYPE_DATA_ERRORS_X: + case OBJECTTYPE_DATA_ERRORS_Y: + case OBJECTTYPE_DATA_ERRORS_Z: + case OBJECTTYPE_DATA_LABELS: + case OBJECTTYPE_DATA_LABEL: + case OBJECTTYPE_AXIS: + case OBJECTTYPE_GRID: + case OBJECTTYPE_SUBGRID: + return true; + default: + break; + } + } + else if ( aSelOID.isAdditionalShape() ) + { + return true; + } + + return false; +} + +bool ChartController::isShapeContext() const +{ + return m_aSelection.isAdditionalShapeSelected() || + ( m_pDrawViewWrapper && m_pDrawViewWrapper->AreObjectsMarked() && + ( m_pDrawViewWrapper->GetCurrentObjIdentifier() == SdrObjKind::Text ) ); +} + +void ChartController::impl_ClearSelection() +{ + if( m_aSelection.hasSelection()) + { + m_aSelection.clearSelection(); + impl_notifySelectionChangeListeners(); + } +} + +bool ChartController::executeDispatch_Delete() +{ + bool bReturn = false; + + // remove the selected object + OUString aCID( m_aSelection.getSelectedCID() ); + if( !aCID.isEmpty() ) + { + if( !isObjectDeleteable( uno::Any( aCID ) ) ) + return false; + + //remove chart object + rtl::Reference< ChartModel > xChartDoc = getChartModel(); + if( !xChartDoc.is() ) + return false; + + ObjectType aObjectType( ObjectIdentifier::getObjectType( aCID )); + switch( aObjectType ) + { + case OBJECTTYPE_TITLE: + { + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId( STR_OBJECT_TITLE )), + m_xUndoManager ); + TitleHelper::removeTitle( + ObjectIdentifier::getTitleTypeForCID( aCID ), getChartModel() ); + bReturn = true; + aUndoGuard.commit(); + break; + } + case OBJECTTYPE_LEGEND: + { + rtl::Reference< Diagram > xDiagram( xChartDoc->getFirstChartDiagram()); + if( xDiagram.is()) + { + rtl::Reference< Legend > xLegend( xDiagram->getLegend2() ); + if( xLegend.is()) + { + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId( STR_OBJECT_LEGEND )), + m_xUndoManager ); + xLegend->setPropertyValue( "Show", uno::Any( false )); + bReturn = true; + aUndoGuard.commit(); + } + } + break; + } + + case OBJECTTYPE_DATA_SERIES: + bReturn = lcl_deleteDataSeries( aCID, getChartModel(), m_xUndoManager ); + break; + + case OBJECTTYPE_LEGEND_ENTRY: + { + ObjectType eParentObjectType = ObjectIdentifier::getObjectType( + ObjectIdentifier::getFullParentParticle( aCID )); + if( eParentObjectType == OBJECTTYPE_DATA_SERIES ) + { + bReturn = lcl_deleteDataSeries( aCID, getChartModel(), m_xUndoManager ); + } + else if( eParentObjectType == OBJECTTYPE_DATA_CURVE ) + { + sal_Int32 nEndPos = aCID.lastIndexOf(':'); + OUString aParentCID = aCID.copy(0, nEndPos); + + bReturn = lcl_deleteDataCurve(aParentCID, getChartModel(), m_xUndoManager ); + } + else if( eParentObjectType == OBJECTTYPE_DATA_AVERAGE_LINE ) + { + executeDispatch_DeleteMeanValue(); + bReturn = true; + } + break; + } + + case OBJECTTYPE_DATA_AVERAGE_LINE: + { + uno::Reference< chart2::XRegressionCurveContainer > xRegCurveCnt( + ObjectIdentifier::getObjectPropertySet( + ObjectIdentifier::getFullParentParticle( aCID ), getChartModel()), uno::UNO_QUERY ); + if( xRegCurveCnt.is()) + { + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId( STR_OBJECT_AVERAGE_LINE )), + m_xUndoManager ); + RegressionCurveHelper::removeMeanValueLine( xRegCurveCnt ); + bReturn = true; + aUndoGuard.commit(); + } + } + break; + + case OBJECTTYPE_DATA_CURVE: + { + bReturn = lcl_deleteDataCurve( aCID, getChartModel(), m_xUndoManager ); + } + break; + + case OBJECTTYPE_DATA_CURVE_EQUATION: + { + uno::Reference< beans::XPropertySet > xEqProp( + ObjectIdentifier::getObjectPropertySet( aCID, getChartModel())); + + if( xEqProp.is()) + { + rtl::Reference<::chart::ChartModel> xModel( getChartModel() ); + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId( STR_OBJECT_CURVE_EQUATION )), + m_xUndoManager ); + { + ControllerLockGuardUNO aCtlLockGuard( xModel ); + xEqProp->setPropertyValue( "ShowEquation", uno::Any( false )); + xEqProp->setPropertyValue( "XName", uno::Any( OUString("x") )); + xEqProp->setPropertyValue( "YName", uno::Any( OUString("f(x)") )); + xEqProp->setPropertyValue( "ShowCorrelationCoefficient", uno::Any( false )); + } + bReturn = true; + aUndoGuard.commit(); + } + } + break; + + case OBJECTTYPE_DATA_ERRORS_X: + case OBJECTTYPE_DATA_ERRORS_Y: + case OBJECTTYPE_DATA_ERRORS_Z: + { + uno::Reference< beans::XPropertySet > xErrorBarProp( + ObjectIdentifier::getObjectPropertySet( aCID, getChartModel() )); + if( xErrorBarProp.is()) + { + TranslateId pId; + + if ( aObjectType == OBJECTTYPE_DATA_ERRORS_X ) + pId = STR_OBJECT_ERROR_BARS_X; + else if ( aObjectType == OBJECTTYPE_DATA_ERRORS_Y ) + pId = STR_OBJECT_ERROR_BARS_Y; + else + pId = STR_OBJECT_ERROR_BARS_Z; + + rtl::Reference<::chart::ChartModel> xModel( getChartModel() ); + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, SchResId(pId)), + m_xUndoManager); + { + ControllerLockGuardUNO aCtlLockGuard( xModel ); + xErrorBarProp->setPropertyValue( + "ErrorBarStyle", + uno::Any( css::chart::ErrorBarStyle::NONE )); + } + bReturn = true; + aUndoGuard.commit(); + } + break; + } + + case OBJECTTYPE_DATA_LABELS: + case OBJECTTYPE_DATA_LABEL: + { + uno::Reference< beans::XPropertySet > xObjectProperties = + ObjectIdentifier::getObjectPropertySet( aCID, getChartModel() ); + if( xObjectProperties.is() ) + { + UndoGuard aUndoGuard( + ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::Delete, + SchResId( aObjectType == OBJECTTYPE_DATA_LABEL ? STR_OBJECT_LABEL : STR_OBJECT_DATALABELS )), + m_xUndoManager ); + chart2::DataPointLabel aLabel; + xObjectProperties->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel; + aLabel.ShowNumber = false; + aLabel.ShowNumberInPercent = false; + aLabel.ShowCategoryName = false; + aLabel.ShowLegendSymbol = false; + aLabel.ShowCustomLabel = false; + aLabel.ShowSeriesName = false; + if( aObjectType == OBJECTTYPE_DATA_LABELS ) + { + rtl::Reference< DataSeries > xSeries = ObjectIdentifier::getDataSeriesForCID( aCID, getChartModel() ); + DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints( xSeries, CHART_UNONAME_LABEL, uno::Any(aLabel) ); + DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints( xSeries, CHART_UNONAME_CUSTOM_LABEL_FIELDS, uno::Any() ); + } + else + { + xObjectProperties->setPropertyValue(CHART_UNONAME_LABEL, uno::Any(aLabel)); + xObjectProperties->setPropertyValue(CHART_UNONAME_CUSTOM_LABEL_FIELDS, uno::Any()); + } + bReturn = true; + aUndoGuard.commit(); + } + break; + } + case OBJECTTYPE_AXIS: + { + executeDispatch_DeleteAxis(); + bReturn = true; + break; + } + case OBJECTTYPE_GRID: + { + executeDispatch_DeleteMajorGrid(); + bReturn = true; + break; + } + case OBJECTTYPE_SUBGRID: + { + executeDispatch_DeleteMinorGrid(); + bReturn = true; + break; + } + + default: + { + break; + } + } + } + else + { + //remove additional shape + impl_ClearSelection(); + { + SolarMutexGuard aSolarGuard; + if ( m_pDrawViewWrapper ) + { + m_pDrawViewWrapper->DeleteMarked(); + bReturn = true; + } + } + } + return bReturn; +} + +void ChartController::executeDispatch_ToggleLegend() +{ + rtl::Reference< ChartModel > xModel = getChartModel(); + UndoGuard aUndoGuard( + SchResId( STR_ACTION_TOGGLE_LEGEND ), m_xUndoManager ); + rtl::Reference< Legend > xLegendProp = LegendHelper::getLegend(*xModel); + bool bChanged = false; + if( xLegendProp.is()) + { + try + { + bool bShow = false; + if( xLegendProp->getPropertyValue( "Show") >>= bShow ) + { + xLegendProp->setPropertyValue( "Show", uno::Any( ! bShow )); + bChanged = true; + } + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + } + else + { + xLegendProp = LegendHelper::getLegend(*xModel, m_xCC, true); + if( xLegendProp.is()) + bChanged = true; + } + + if( bChanged ) + aUndoGuard.commit(); +} + +void ChartController::executeDispatch_ToggleGridHorizontal() +{ + UndoGuard aUndoGuard( + SchResId( STR_ACTION_TOGGLE_GRID_HORZ ), m_xUndoManager ); + rtl::Reference< Diagram > xDiagram( getFirstDiagram() ); + if( !xDiagram.is()) + return; + + sal_Int32 nDimensionIndex = 1; + sal_Int32 nCooSysIndex = 0; + + bool bHasMajorYGrid = AxisHelper::isGridShown( nDimensionIndex, nCooSysIndex, true, xDiagram ); + bool bHasMinorYGrid = AxisHelper::isGridShown( nDimensionIndex, nCooSysIndex, false, xDiagram ); + + if( bHasMajorYGrid ) + { + if ( bHasMinorYGrid ) + { + AxisHelper::hideGrid( nDimensionIndex, nCooSysIndex, true, xDiagram ); + AxisHelper::hideGrid( nDimensionIndex, nCooSysIndex, false, xDiagram ); + } + else + { + AxisHelper::showGrid( nDimensionIndex, nCooSysIndex, false, xDiagram ); + } + } + else + { + AxisHelper::showGrid( nDimensionIndex, nCooSysIndex, true, xDiagram ); + } + aUndoGuard.commit(); +} + +void ChartController::executeDispatch_ToggleGridVertical() +{ + UndoGuard aUndoGuard( + SchResId( STR_ACTION_TOGGLE_GRID_VERTICAL ), m_xUndoManager ); + rtl::Reference< Diagram > xDiagram( getFirstDiagram() ); + if( !xDiagram.is()) + return; + + sal_Int32 nDimensionIndex = 0; + sal_Int32 nCooSysIndex = 0; + + bool bHasMajorXGrid = AxisHelper::isGridShown( nDimensionIndex, nCooSysIndex, true, xDiagram ); + bool bHasMinorXGrid = AxisHelper::isGridShown( nDimensionIndex, nCooSysIndex, false, xDiagram ); + if( bHasMajorXGrid ) + { + if (bHasMinorXGrid) + { + AxisHelper::hideGrid( nDimensionIndex, nCooSysIndex, true, xDiagram ); + AxisHelper::hideGrid( nDimensionIndex, nCooSysIndex, false, xDiagram ); + } + else + { + AxisHelper::showGrid( nDimensionIndex, nCooSysIndex, false, xDiagram ); + } + } + else + { + AxisHelper::showGrid( nDimensionIndex, nCooSysIndex, true, xDiagram ); + } + + aUndoGuard.commit(); +} + +void ChartController::executeDispatch_FillColor(sal_uInt32 nColor) +{ + try + { + OUString aCID( m_aSelection.getSelectedCID() ); + rtl::Reference<::chart::ChartModel> xChartModel = getChartModel(); + if( xChartModel.is() ) + { + Reference< beans::XPropertySet > xPointProperties( + ObjectIdentifier::getObjectPropertySet( aCID, xChartModel ) ); + if( xPointProperties.is() ) + xPointProperties->setPropertyValue( "FillColor", uno::Any( nColor ) ); + } + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION( "chart2" ); + } +} + +void ChartController::executeDispatch_FillGradient(std::u16string_view sJSONGradient) +{ + basegfx::BGradient aBGradient = basegfx::BGradient::fromJSON(sJSONGradient); + css::awt::Gradient aGradient = model::gradient::createUnoGradient2(aBGradient); + + try + { + OUString aCID( m_aSelection.getSelectedCID() ); + rtl::Reference<::chart::ChartModel> xChartModel = getChartModel(); + + if( xChartModel.is() ) + { + Reference< beans::XPropertySet > xPropSet( + ObjectIdentifier::getObjectPropertySet( aCID, xChartModel ) ); + + if( xPropSet.is() ) + { + OUString aPrefferedName = + OUString::number(static_cast<sal_Int32>(Color(aBGradient.GetColorStops().front().getStopColor()))) + + OUString::number(static_cast<sal_Int32>(Color(aBGradient.GetColorStops().back().getStopColor()))) + + OUString::number(static_cast<sal_Int32>(aBGradient.GetAngle().get())); + + OUString aNewName = PropertyHelper::addGradientUniqueNameToTable(css::uno::Any(aGradient), + xChartModel, + aPrefferedName); + + xPropSet->setPropertyValue("FillGradientName", css::uno::Any(aNewName)); + } + } + } + catch( const uno::Exception & ) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +void ChartController::executeDispatch_LineColor(sal_uInt32 nColor) +{ + try + { + OUString aCID( m_aSelection.getSelectedCID() ); + rtl::Reference<::chart::ChartModel> xChartModel = getChartModel(); + if( xChartModel.is() ) + { + Reference< beans::XPropertySet > xPropSet( + ObjectIdentifier::getObjectPropertySet( aCID, xChartModel ) ); + + ObjectType eType = ObjectIdentifier::getObjectType(aCID); + if (eType == OBJECTTYPE_DIAGRAM) + { + css::uno::Reference<css::chart2::XDiagram> xDiagram( + xPropSet, css::uno::UNO_QUERY); + if (xDiagram.is()) + xPropSet.set(xDiagram->getWall()); + } + + if( xPropSet.is() ) + xPropSet->setPropertyValue( "LineColor", css::uno::Any( Color(ColorTransparency, nColor) ) ); + } + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION( "chart2" ); + } +} + +void ChartController::executeDispatch_LineWidth(sal_uInt32 nWidth) +{ + try + { + OUString aCID( m_aSelection.getSelectedCID() ); + rtl::Reference<::chart::ChartModel> xChartModel = getChartModel(); + if( xChartModel.is() ) + { + Reference< beans::XPropertySet > xPropSet( + ObjectIdentifier::getObjectPropertySet( aCID, xChartModel ) ); + + ObjectType eType = ObjectIdentifier::getObjectType(aCID); + if (eType == OBJECTTYPE_DIAGRAM) + { + css::uno::Reference<css::chart2::XDiagram> xDiagram( + xPropSet, css::uno::UNO_QUERY); + if (xDiagram.is()) + xPropSet.set(xDiagram->getWall()); + } + + if( xPropSet.is() ) + xPropSet->setPropertyValue( "LineWidth", css::uno::Any( nWidth ) ); + } + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION( "chart2" ); + } +} + +void ChartController::executeDispatch_LOKSetTextSelection(int nType, int nX, int nY) +{ + if (!m_pDrawViewWrapper) + return; + + if (!m_pDrawViewWrapper->IsTextEdit()) + return; + + OutlinerView* pOutlinerView = m_pDrawViewWrapper->GetTextEditOutlinerView(); + if (!pOutlinerView) + return; + + EditView& rEditView = pOutlinerView->GetEditView(); + Point aPoint(convertTwipToMm100(nX), convertTwipToMm100(nY)); + switch (nType) + { + case LOK_SETTEXTSELECTION_START: + rEditView.SetCursorLogicPosition(aPoint, /*bPoint=*/false, /*bClearMark=*/false); + break; + case LOK_SETTEXTSELECTION_END: + rEditView.SetCursorLogicPosition(aPoint, /*bPoint=*/true, /*bClearMark=*/false); + break; + case LOK_SETTEXTSELECTION_RESET: + rEditView.SetCursorLogicPosition(aPoint, /*bPoint=*/true, /*bClearMark=*/true); + break; + default: + assert(false); + break; + } +} + +void ChartController::executeDispatch_LOKPieSegmentDragging( int nOffset ) +{ + try + { + OUString aCID( m_aSelection.getSelectedCID() ); + rtl::Reference<::chart::ChartModel> xChartModel = getChartModel(); + if( xChartModel.is() ) + { + Reference< beans::XPropertySet > xPointProperties( + ObjectIdentifier::getObjectPropertySet( aCID, xChartModel ) ); + if( xPointProperties.is() ) + xPointProperties->setPropertyValue( "Offset", uno::Any( nOffset / 100.0 ) ); + } + } + catch( const uno::Exception & ) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } +} + +void ChartController::impl_ShapeControllerDispatch( const util::URL& rURL, const Sequence< beans::PropertyValue >& rArgs ) +{ + Reference< frame::XDispatch > xDispatch( m_aDispatchContainer.getShapeController() ); + if ( xDispatch.is() ) + { + xDispatch->dispatch( rURL, rArgs ); + } +} + +void ChartController::impl_switchDiagramPositioningToExcludingPositioning() +{ + UndoGuard aUndoGuard( ActionDescriptionProvider::createDescription( + ActionDescriptionProvider::ActionType::PosSize, + ObjectNameProvider::getName( OBJECTTYPE_DIAGRAM)), + m_xUndoManager ); + if (DiagramHelper::switchDiagramPositioningToExcludingPositioning(*getChartModel(), true, true)) + aUndoGuard.commit(); +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartController_Window.cxx b/chart2/source/controller/main/ChartController_Window.cxx new file mode 100644 index 0000000000..1bdb1f2ed4 --- /dev/null +++ b/chart2/source/controller/main/ChartController_Window.cxx @@ -0,0 +1,2103 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <ChartController.hxx> +#include <ChartView.hxx> +#include <PositionAndSizeHelper.hxx> +#include <ObjectIdentifier.hxx> +#include <ChartWindow.hxx> +#include <ResId.hxx> +#include <ChartModel.hxx> +#include <ChartModelHelper.hxx> +#include <ChartType.hxx> +#include <DiagramHelper.hxx> +#include <Diagram.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 <RegressionCurveModel.hxx> +#include <StatisticsHelper.hxx> +#include <DataSeries.hxx> +#include <DataSeriesHelper.hxx> +#include <DataSeriesProperties.hxx> +#include <Axis.hxx> +#include <AxisHelper.hxx> +#include <LegendHelper.hxx> +#include <servicenames_charttypes.hxx> +#include "DrawCommandDispatch.hxx" +#include <PopupRequest.hxx> +#include "ControllerCommandDispatch.hxx" + +#include <com/sun/star/chart2/RelativePosition.hpp> +#include <com/sun/star/chart2/RelativeSize.hpp> +#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp> + +#include <com/sun/star/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 <comphelper/lok.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequence.hxx> + +#include <sfx2/viewsh.hxx> +#include <svx/ActionDescriptionProvider.hxx> +#include <svx/obj3d.hxx> +#include <svx/scene3d.hxx> +#include <svx/svddrgmt.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/weld.hxx> +#include <vcl/ptrstyle.hxx> +#include <svtools/acceleratorexecute.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <toolkit/awt/vclxmenu.hxx> +#include <sal/log.hxx> +#include <o3tl/string_view.hxx> + +#include <boost/property_tree/json_parser.hpp> +#include <sfx2/dispatch.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> + +#define DRGPIX 2 // Drag MinMove in Pixel + +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using namespace ::chart::DataSeriesProperties; +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( std::u16string_view 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)) + return; + + 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( getChartModel() ); + 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% + if( m_xChartView.is() ) + { + auto aZoomFactors(::comphelper::InitPropertySequence({ + { "ScaleXNumerator", uno::Any( nScaleXNumerator ) }, + { "ScaleXDenominator", uno::Any( nScaleXDenominator ) }, + { "ScaleYNumerator", uno::Any( nScaleYNumerator ) }, + { "ScaleYDenominator", uno::Any( nScaleYDenominator ) } + })); + m_xChartView->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->GetOutDev()->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 + { + rtl::Reference<ChartModel> xModel(getChartModel()); + //OSL_ENSURE( xModel.is(), "ChartController::execute_Paint: have no model to paint"); + if (!xModel.is()) + return; + + //better performance for big data + if (m_xChartView.is()) + { + awt::Size aResolution(1000, 1000); + { + SolarMutexGuard aGuard; + auto pChartWindow(GetChartWindow()); + if (pChartWindow) + { + aResolution.Width = pChartWindow->GetSizePixel().Width(); + aResolution.Height = pChartWindow->GetSizePixel().Height(); + } + } + m_xChartView->setPropertyValue( "Resolution", uno::Any( aResolution )); + } + + if (m_xChartView.is()) + m_xChartView->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( ... ) + { + } +} + +static 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() ) + return; + + 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->GetOutDev()); + 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() == SdrObjKind::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( getChartModel() ) ) + { + 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(), getChartModel(), eRotationDirection ); + } + } + else + { + std::u16string_view aDragMethodServiceName( ObjectIdentifier::getDragMethodServiceName( m_aSelection.getSelectedCID() ) ); + if( aDragMethodServiceName == ObjectIdentifier::getPieSegmentDragMethodServiceName() ) + pDragMethod = new DragMethod_PieSegment( *pDrawViewWrapper, m_aSelection.getSelectedCID(), getChartModel() ); + } + 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->GetOutDev()) ) + return; + } + + if(pDrawViewWrapper->IsAction()) + { + pDrawViewWrapper->MovAction( pChartWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + } + + impl_SetMousePointer( rMEvt ); +} + +void ChartController::execute_MouseButtonUp( const MouseEvent& rMEvt ) +{ + ControllerLockGuardUNO aCLGuard( getChartModel() ); + 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->GetOutDev()) ) + 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() == SdrObjKind::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(); + tools::Rectangle aOldObjectRect = pObj->GetLastBoundRect(); + awt::Size aPageSize( ChartModelHelper::getPageSize( getChartModel() ) ); + tools::Rectangle aPageRect( 0,0,aPageSize.Width,aPageSize.Height ); + + const E3dObject* pE3dObject(DynCastE3dObject(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; + rtl::Reference< ChartModel > xModel = getChartModel(); + if ( eObjectType == OBJECTTYPE_LEGEND ) + bChanged = DiagramHelper::switchDiagramPositioningToExcludingPositioning( *xModel, false , true ); + + bool bMoved = PositionAndSizeHelper::moveObject( m_aSelection.getSelectedCID() + , xModel + , awt::Rectangle(aObjectRect.Left(),aObjectRect.Top(),aObjectRect.getOpenWidth(),aObjectRect.getOpenHeight()) + , awt::Rectangle(aOldObjectRect.Left(), aOldObjectRect.Top(), 0, 0) + , awt::Rectangle(aPageRect.Left(),aPageRect.Top(),aPageRect.getOpenWidth(),aPageRect.getOpenHeight()) ); + + 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( getChartModel() ); + + //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(); + } + + //@todo ForcePointer(&rMEvt); + pChartWindow->ReleaseMouse(); + + // 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 ); + } + + if( m_aSelection.isSelectionDifferentFromBeforeMouseDown() ) + bNotifySelectionChange = true; + } + + impl_SetMousePointer( rMEvt ); + + if(bNotifySelectionChange) + impl_notifySelectionChangeListeners(); +} + +void ChartController::execute_DoubleClick( const Point* pMousePixel ) +{ + const SfxViewShell* pViewShell = SfxViewShell::Current(); + bool isMobilePhone = pViewShell && pViewShell->isLOKMobilePhone(); + if (isMobilePhone) + return; + + 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 ( DynCastSdrTextObj(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(); + + rtl::Reference< VCLXPopupMenu > xPopupMenu = new VCLXPopupMenu(); + + 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() ? std::u16string_view( u"drawtext" ) : std::u16string_view( u"draw" ); + else + { + ObjectType eObjectType = ObjectIdentifier::getObjectType( m_aSelection.getSelectedCID() ); + + // todo: the context menu should be specified by an xml file in uiconfig + sal_Int16 nUniqueId = 1; + if (eObjectType != OBJECTTYPE_DATA_TABLE) + { + lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:Cut" ); + lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:Copy" ); + lcl_insertMenuCommand( xPopupMenu, nUniqueId++, ".uno:Paste" ); + xPopupMenu->insertSeparator( -1 ); + } + + rtl::Reference< Diagram > xDiagram = getFirstDiagram(); + + 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 ); + rtl::Reference< DataSeries > xSeries = ObjectIdentifier::getDataSeriesForCID( m_aSelection.getSelectedCID(), getChartModel() ); + rtl::Reference< RegressionCurveModel > xTrendline = RegressionCurveHelper::getFirstCurveNotMeanValueLine( xSeries ); + bool bHasEquation = RegressionCurveHelper::hasEquation( xTrendline ); + rtl::Reference< RegressionCurveModel > xMeanValue = RegressionCurveHelper::getMeanValueLine( xSeries ); + 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; + + if( xSeries.is() ) + { + uno::Sequence< sal_Int32 > aAttributedDataPointIndexList; + // "AttributedDataPoints" + if( xSeries->getFastPropertyValue( PROP_DATASERIES_ATTRIBUTED_DATA_POINTS ) >>= aAttributedDataPointIndexList ) + { + if( aAttributedDataPointIndexList.hasElements() ) + { + if( bIsPoint ) + { + auto aIt = std::find( std::as_const(aAttributedDataPointIndexList).begin(), std::as_const(aAttributedDataPointIndexList).end(), nPointIndex ); + if( aIt != std::as_const(aAttributedDataPointIndexList).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" ); + } + + rtl::Reference< ChartType > xChartType( xDiagram->getChartTypeOfSeries( xSeries ) ); + if( xChartType->getChartType() == CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK ) + { + try + { + bool bJapaneseStyle = false; + xChartType->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" ); + rtl::Reference< VCLXPopupMenu > xArrangePopupMenu = new VCLXPopupMenu(); + 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 ) + { + rtl::Reference< Axis > xAxis = ObjectIdentifier::getAxisForCID( m_aSelection.getSelectedCID(), getChartModel() ); + 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 = !TitleHelper::getCompleteString( xAxis->getTitleObject2() ).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 (bIsAxisVisible) + lcl_insertMenuCommand(xPopupMenu, nUniqueId++, ".uno:InsertDataTable"); + } + } + else if (eObjectType == OBJECTTYPE_DATA_TABLE) + { + lcl_insertMenuCommand(xPopupMenu, nUniqueId++, ".uno:DeleteDataTable"); + } + + 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{ + css::uno::Any(comphelper::makePropertyValue( "IsContextMenu", true )), + css::uno::Any(comphelper::makePropertyValue( "Frame", m_xFrame )), + css::uno::Any(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; + + xPopupController->setPopupMenu( xPopupMenu ); + + if (comphelper::LibreOfficeKit::isActive()) + { + if (SfxViewShell* pViewShell = SfxViewShell::Current()) + { + ControllerCommandDispatch* pCommandDispatch = dynamic_cast<ControllerCommandDispatch*>(m_aDispatchContainer.getChartDispatcher().get()); + if (pCommandDispatch) + { + for (int nPos = 0, nCount = xPopupMenu->getItemCount(); nPos < nCount; ++nPos) + { + auto nItemId = xPopupMenu->getItemId(nPos); + OUString aCommandURL = xPopupMenu->getCommand(nItemId); + if (!pCommandDispatch->commandAvailable(aCommandURL)) + xPopupMenu->enableItem(nItemId, false); + } + } + + boost::property_tree::ptree aMenu = SfxDispatcher::fillPopupMenu(xPopupMenu); + boost::property_tree::ptree aRoot; + aRoot.add_child("menu", aMenu); + + std::stringstream aStream; + boost::property_tree::write_json(aStream, aRoot, true); + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_MENU, OString(aStream.str())); + } + } + else + { + 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 + 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 && m_xFrame.is() && m_xCC.is()) + { + m_apAccelExecute = ::svt::AcceleratorExecute::createAcceleratorHelper(); + OSL_ASSERT(m_apAccelExecute); + if (m_apAccelExecute) + 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) + 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) + rtl::Reference<::chart::ChartModel> xChartDoc( getChartModel() ); + ObjectKeyNavigation aObjNav( m_aSelection.getSelectedOID(), xChartDoc, m_xChartView.get() ); + awt::KeyEvent aKeyEvent( ::svt::AcceleratorExecute::st_VCLKey2AWTKey( aKeyCode )); + bReturn = aObjNav.handleKeyEvent( aKeyEvent ); + if( bReturn ) + { + const 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(), getChartModel() ) ) + { + 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; + std::u16string_view 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) + { + // together with Alt-key: 1 px in each direction + 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) + { + // together with Alt-key: 1 px + 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 + if (eObjectType == OBJECTTYPE_DATA_LABEL) + { + SdrObject* pObj = pDrawViewWrapper->getSelectedObject(); + if (pObj) + { + tools::Rectangle aRect = pObj->GetSnapRect(); + awt::Size aPageSize(ChartModelHelper::getPageSize(getChartModel())); + if ((fShiftAmountX > 0.0 && (aRect.Right() + fShiftAmountX > aPageSize.Width)) || + (fShiftAmountX < 0.0 && (aRect.Left() + fShiftAmountX < 0)) || + (fShiftAmountY > 0.0 && (aRect.Bottom() + fShiftAmountY > aPageSize.Height)) || + (fShiftAmountY < 0.0 && (aRect.Top() + fShiftAmountY < 0))) + bReturn = false; + else + bReturn = PositionAndSizeHelper::moveObject( + m_aSelection.getSelectedCID(), getChartModel(), + awt::Rectangle(aRect.Left() + fShiftAmountX, aRect.Top() + fShiftAmountY, aRect.getOpenWidth(), aRect.getOpenHeight()), + awt::Rectangle(aRect.Left(), aRect.Top(), 0, 0), + awt::Rectangle(0, 0, aPageSize.Width, aPageSize.Height)); + } + } + else + 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( getChartModel() ) ); + aPos.X = static_cast< tools::Long >( static_cast< double >( aPos.X ) + fShiftAmountX ); + aPos.Y = static_cast< tools::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) + { + rtl::Reference< ChartModel > xChartModel = getChartModel(); + if(xChartModel.is()) + { + OUString aDump = xChartModel->dump("shapes"); + 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->GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SchResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + } + + return bReturn; +} + +bool ChartController::requestQuickHelp( + ::Point aAtLogicPosition, + bool bIsBalloonHelp, + OUString & rOutQuickHelpText, + awt::Rectangle & rOutEqualRect ) +{ + rtl::Reference<::chart::ChartModel> xChartModel; + if( m_aModel.is()) + xChartModel = getChartModel(); + 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 + if( m_xChartView ) + rOutEqualRect = m_xChartView->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 + std::unique_lock aGuard2(m_aLifeTimeManager.m_aAccessMutex); + m_aLifeTimeManager.m_aSelectionChangeListeners.addInterface( aGuard2, 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 + std::unique_lock aGuard2(m_aLifeTimeManager.m_aAccessMutex); + m_aLifeTimeManager.m_aSelectionChangeListeners.removeInterface( aGuard2, xListener ); +} + +void ChartController::impl_notifySelectionChangeListeners() +{ + std::unique_lock aGuard(m_aLifeTimeManager.m_aAccessMutex); + if( m_aLifeTimeManager.m_aSelectionChangeListeners.getLength(aGuard) ) + { + uno::Reference< view::XSelectionSupplier > xSelectionSupplier(this); + lang::EventObject aEvent( xSelectionSupplier ); + m_aLifeTimeManager.m_aSelectionChangeListeners.notifyEach(aGuard, &view::XSelectionChangeListener::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 ); + + rtl::Reference<::chart::ChartModel> xChartModel( getChartModel() ); + 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( m_xChartView.get() ); + 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( std::u16string_view rCID, double fAdditionalOffset ) +{ + bool bResult = false; + if( fAdditionalOffset < -1.0 || fAdditionalOffset > 1.0 || fAdditionalOffset == 0.0 ) + return bResult; + + sal_Int32 nDataPointIndex = ObjectIdentifier::getIndexFromParticleOrCID( rCID ); + rtl::Reference< DataSeries > xSeries = + ObjectIdentifier::getDataSeriesForCID( rCID, getChartModel() ); + 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(PointerStyle::Arrow); + return; + } + + if ( m_pDrawViewWrapper->IsTextEdit() ) + { + if( m_pDrawViewWrapper->IsTextEditHit( aMousePos ) ) + { + pChartWindow->SetPointer( m_pDrawViewWrapper->GetPreferredPointer( + aMousePos, pChartWindow->GetOutDev(), 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 ) + { + PointerStyle aPointer = m_pDrawViewWrapper->GetPreferredPointer( + aMousePos, pChartWindow->GetOutDev(), nModifier, bLeftDown ); + bool bForceArrowPointer = false; + + ObjectIdentifier aOID( m_aSelection.getSelectedOID() ); + + switch( aPointer) + { + 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( 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 = m_pDrawViewWrapper->GetCurrentObjIdentifier(); + switch ( eKind ) + { + case SdrObjKind::Line: + { + ePointerStyle = PointerStyle::DrawLine; + } + break; + case SdrObjKind::Rectangle: + case SdrObjKind::CustomShape: + { + ePointerStyle = PointerStyle::DrawRect; + } + break; + case SdrObjKind::CircleOrEllipse: + { + ePointerStyle = PointerStyle::DrawEllipse; + } + break; + case SdrObjKind::FreehandLine: + { + ePointerStyle = PointerStyle::DrawPolygon; + } + break; + case SdrObjKind::Text: + { + ePointerStyle = PointerStyle::DrawText; + } + break; + case SdrObjKind::Caption: + { + ePointerStyle = PointerStyle::DrawCaption; + } + break; + default: + { + ePointerStyle = PointerStyle::DrawRect; + } + break; + } + pChartWindow->SetPointer( ePointerStyle ); + return; + } + + OUString aHitObjectCID( + SelectionHelper::getHitObjectCID( + aMousePos, *m_pDrawViewWrapper, true /*bGetDiagramInsteadOf_Wall*/ )); + + if( m_pDrawViewWrapper->IsTextEdit() ) + { + if( aHitObjectCID == m_aSelection.getSelectedCID() ) + { + pChartWindow->SetPointer( 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 + , getChartModel() ) ) + pChartWindow->SetPointer( 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( PointerStyle::Arrow ); + return; + } + } + pChartWindow->SetPointer( PointerStyle::Move ); + } + } + else + pChartWindow->SetPointer( PointerStyle::Arrow ); +} + +void ChartController::sendPopupRequest(std::u16string_view rCID, tools::Rectangle aRectangle) +{ + ChartModel* pChartModel = 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(); + + css::uno::Reference<css::awt::XRequestCallback> xPopupRequest = pChartModel->getPopupRequest(); + PopupRequest* pPopupRequest = dynamic_cast<PopupRequest*>(xPopupRequest.get()); + if (!pPopupRequest) + return; + + // Get dimension index from CID + size_t nStartPos = rCID.rfind('.'); + nStartPos++; + sal_Int32 nEndPos = rCID.size(); + std::u16string_view sDimensionIndex = rCID.substr(nStartPos, nEndPos - nStartPos); + sal_Int32 nDimensionIndex = o3tl::toInt32(sDimensionIndex); + + 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::Any(xRectangle)}, + {"DimensionIndex", uno::Any(sal_Int32(nDimensionIndex))}, + {"PivotTableName", uno::Any(sPivotTableName)}, + }); + + pPopupRequest->getCallback()->notify(uno::Any(aCallbackData)); +} + +} //namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartDropTargetHelper.cxx b/chart2/source/controller/main/ChartDropTargetHelper.cxx new file mode 100644 index 0000000000..fc6762598a --- /dev/null +++ b/chart2/source/controller/main/ChartDropTargetHelper.cxx @@ -0,0 +1,178 @@ +/* -*- 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 "ChartDropTargetHelper.hxx" +#include <DataSource.hxx> +#include <DataSourceHelper.hxx> +#include <ChartModel.hxx> +#include <Diagram.hxx> + +#include <com/sun/star/chart2/data/XDataProvider.hpp> + +#include <sot/formats.hxx> +#include <utility> +#include <vector> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace +{ + +std::vector< OUString > lcl_getStringsFromByteSequence( + const Sequence< sal_Int8 > & aByteSequence ) +{ + std::vector< OUString > aResult; + const sal_Int32 nLength = aByteSequence.getLength(); + const char * pBytes( reinterpret_cast< const char* >( aByteSequence.getConstArray())); + sal_Int32 nStartPos = 0; + for( sal_Int32 nPos=0; nPos<nLength; ++nPos ) + { + if( pBytes[nPos] == '\0' ) + { + aResult.emplace_back( pBytes + nStartPos, (nPos - nStartPos), RTL_TEXTENCODING_ASCII_US ); + nStartPos = nPos + 1; + } + } + return aResult; +} + +} // anonymous namespace + +namespace chart +{ + +ChartDropTargetHelper::ChartDropTargetHelper( + const Reference< datatransfer::dnd::XDropTarget >& rxDropTarget, + rtl::Reference<::chart::ChartModel> xChartDocument ) : + DropTargetHelper( rxDropTarget ), + m_xChartDocument(std::move( xChartDocument )) +{} + +ChartDropTargetHelper::~ChartDropTargetHelper() +{} + +bool ChartDropTargetHelper::satisfiesPrerequisites() const +{ + return ( m_xChartDocument.is() && + ! m_xChartDocument->hasInternalDataProvider()); +} + +sal_Int8 ChartDropTargetHelper::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + sal_Int8 nResult = DND_ACTION_NONE; + + if( ( rEvt.mnAction == DND_ACTION_COPY || + rEvt.mnAction == DND_ACTION_MOVE ) && + satisfiesPrerequisites() && + IsDropFormatSupported( SotClipboardFormatId::LINK ) ) + { + // @todo: check if the data is suitable. Is this possible without XTransferable? + nResult = rEvt.mnAction; + } + + return nResult; +} + +sal_Int8 ChartDropTargetHelper::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + sal_Int8 nResult = DND_ACTION_NONE; + + if( ( rEvt.mnAction == DND_ACTION_COPY || + rEvt.mnAction == DND_ACTION_MOVE ) && + rEvt.maDropEvent.Transferable.is() && + satisfiesPrerequisites()) + { + TransferableDataHelper aDataHelper( rEvt.maDropEvent.Transferable ); + if( aDataHelper.HasFormat( SotClipboardFormatId::LINK )) + { + Sequence<sal_Int8> aBytes = aDataHelper.GetSequence(SotClipboardFormatId::LINK, OUString()); + if (aBytes.hasElements()) + { + std::vector< OUString > aStrings( lcl_getStringsFromByteSequence( aBytes )); + if( aStrings.size() >= 3 && aStrings[0] == "soffice" ) + { + OUString aRangeString( aStrings[2] ); + if( m_xChartDocument.is()) + { + Reference< frame::XModel > xParentModel( m_xChartDocument->getParent(), uno::UNO_QUERY ); + if( xParentModel.is() && + m_xChartDocument.is()) + { + // @todo: get the title somehow and compare it to + // aDocName if successful (the document is the + // parent) + rtl::Reference< Diagram > xDiagram = m_xChartDocument->getFirstChartDiagram(); + Reference< chart2::data::XDataProvider > xDataProvider( m_xChartDocument->getDataProvider()); + if( xDataProvider.is() && xDiagram.is() && + DataSourceHelper::allArgumentsForRectRangeDetected( m_xChartDocument )) + { + rtl::Reference< DataSource > xDataSource1 = + DataSourceHelper::pressUsedDataIntoRectangularFormat( m_xChartDocument ); + Sequence< beans::PropertyValue > aArguments( + xDataProvider->detectArguments( xDataSource1 )); + + OUString aOldRange; + beans::PropertyValue * pCellRange = nullptr; + for( sal_Int32 i=0; i<aArguments.getLength(); ++i ) + { + if ( aArguments[i].Name == "CellRangeRepresentation" ) + { + pCellRange = (aArguments.getArray() + i); + aArguments[i].Value >>= aOldRange; + break; + } + } + if( pCellRange ) + { + // copy means add ranges, move means replace + if( rEvt.mnAction == DND_ACTION_COPY ) + { + // @todo: using implicit knowledge that ranges can be + // merged with ";". This should be done more general + pCellRange->Value <<= aOldRange + ";" + aRangeString; + } + // move means replace range + else + { + pCellRange->Value <<= aRangeString; + } + + Reference< chart2::data::XDataSource > xDataSource2 = + xDataProvider->createDataSource( aArguments ); + xDiagram->setDiagramData( xDataSource2, aArguments ); + + // always return copy state to avoid deletion of the dragged range + nResult = DND_ACTION_COPY; + } + } + } + } + } + } + } + } + return nResult; +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartDropTargetHelper.hxx b/chart2/source/controller/main/ChartDropTargetHelper.hxx new file mode 100644 index 0000000000..ab573f1cd6 --- /dev/null +++ b/chart2/source/controller/main/ChartDropTargetHelper.hxx @@ -0,0 +1,56 @@ +/* -*- 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 . + */ +#pragma once + +#include <vcl/transfer.hxx> +#include <rtl/ref.hxx> + +namespace com::sun::star { + namespace chart2 { + class XChartDocument; + } +} + +namespace chart +{ +class ChartModel; + +class ChartDropTargetHelper : public DropTargetHelper +{ +public: + ChartDropTargetHelper() = delete; + explicit ChartDropTargetHelper( + const css::uno::Reference< css::datatransfer::dnd::XDropTarget >& rxDropTarget, + rtl::Reference<::chart::ChartModel> xChartDocument ); + virtual ~ChartDropTargetHelper() override; + +protected: + + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + +private: + bool satisfiesPrerequisites() const; + + rtl::Reference<::chart::ChartModel> m_xChartDocument; +}; + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartFrameloader.cxx b/chart2/source/controller/main/ChartFrameloader.cxx new file mode 100644 index 0000000000..2ff4558800 --- /dev/null +++ b/chart2/source/controller/main/ChartFrameloader.cxx @@ -0,0 +1,185 @@ +/* -*- 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 "ChartFrameloader.hxx" +#include <servicenames.hxx> +#include <MediaDescriptorHelper.hxx> +#include <ChartController.hxx> +#include <ChartModel.hxx> +#include <unotools/fcm.hxx> +#include <unotools/mediadescriptor.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/frame/XLoadable.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/diagnose_ex.hxx> + +namespace chart +{ + +using namespace ::com::sun::star; + +ChartFrameLoader::ChartFrameLoader( + uno::Reference<uno::XComponentContext> const & xContext) + : m_bCancelRequired( false ) +{ + m_xCC = xContext; + m_oCancelFinished.reset(); +} + +ChartFrameLoader::~ChartFrameLoader() +{ +} + +bool ChartFrameLoader::impl_checkCancel() +{ + if(m_bCancelRequired) + { + m_oCancelFinished.set(); + return true; + } + return false; +} + +// lang::XServiceInfo + +OUString SAL_CALL ChartFrameLoader::getImplementationName() +{ + return CHART_FRAMELOADER_SERVICE_IMPLEMENTATION_NAME; +} + +sal_Bool SAL_CALL ChartFrameLoader::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL ChartFrameLoader::getSupportedServiceNames() +{ + return { CHART_FRAMELOADER_SERVICE_NAME }; +} + +// frame::XFrameLoader + +sal_Bool SAL_CALL ChartFrameLoader::load( const uno::Sequence< beans::PropertyValue >& rMediaDescriptor, const uno::Reference<frame::XFrame >& xFrame ) +{ + //@todo ? need to add as terminate listener to desktop? + + uno::Reference< frame::XModel > xModel; + bool bHaveLoadedModel = false; + + utl::MediaDescriptor aMediaDescriptor(rMediaDescriptor); + { + utl::MediaDescriptor::const_iterator aIt( aMediaDescriptor.find( utl::MediaDescriptor::PROP_MODEL)); + if( aIt != aMediaDescriptor.end()) + { + xModel.set( (*aIt).second.get< uno::Reference< frame::XModel > >()); + bHaveLoadedModel = true; + } + } + + //create and initialize the model + if( ! xModel.is()) + { + //@todo?? load mechanism to cancel during loading of document + xModel = new ChartModel(m_xCC); + + if( impl_checkCancel() ) + return false; + } + + //create the controller(+XWindow) + rtl::Reference< ChartController > xController = new ChartController(m_xCC); + + if( impl_checkCancel() ) + return false; + + //connect frame, controller and model one to each other: + if(xModel.is()) + { + utl::ConnectFrameControllerModel(xFrame, xController, xModel); + } + + // call initNew() or load() at XLoadable + if(bHaveLoadedModel) + return true; + + try + { + utl::MediaDescriptor::const_iterator aIt( aMediaDescriptor.find( utl::MediaDescriptor::PROP_URL)); + if( aIt != aMediaDescriptor.end()) + { + OUString aURL( (*aIt).second.get< OUString >()); + if( aURL.startsWith( "private:factory/schart" ) ) + { + // create new file + uno::Reference< frame::XLoadable > xLoadable( xModel, uno::UNO_QUERY_THROW ); + xLoadable->initNew(); + } + else + { + // use the URL as BaseURL, similar to what SfxBaseModel effectively does + if (!aURL.isEmpty()) + { + aMediaDescriptor[utl::MediaDescriptor::PROP_DOCUMENTBASEURL] <<= aURL; + } + aMediaDescriptor.addInputStream(); + uno::Sequence< beans::PropertyValue > aCompleteMediaDescriptor; + aMediaDescriptor >> aCompleteMediaDescriptor; + apphelper::MediaDescriptorHelper aMDHelper( aCompleteMediaDescriptor ); + + // load file + // @todo: replace: aMediaDescriptorHelper.getReducedForModel() + uno::Reference< frame::XLoadable > xLoadable( xModel, uno::UNO_QUERY_THROW ); + xLoadable->load( aCompleteMediaDescriptor ); + + //resize standalone files to get correct size: + if( aMDHelper.ISSET_FilterName && aMDHelper.FilterName == "StarChart 5.0" ) + { + uno::Reference<awt::XWindow> xComponentWindow = xController->getComponentWindow(); + awt::Rectangle aRect( xComponentWindow->getPosSize() ); + xComponentWindow->setPosSize( aRect.X, aRect.Y, aRect.Width, aRect.Height, 0 ); + } + } + } + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + + return true; +} + +void SAL_CALL ChartFrameLoader::cancel() +{ + m_oCancelFinished.reset(); + m_bCancelRequired = true; + m_oCancelFinished.wait(); + m_bCancelRequired = false; +} + +} //namespace chart + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_chart2_ChartFrameLoader_get_implementation(css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new chart::ChartFrameLoader(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartFrameloader.hxx b/chart2/source/controller/main/ChartFrameloader.hxx new file mode 100644 index 0000000000..99f0ffd069 --- /dev/null +++ b/chart2/source/controller/main/ChartFrameloader.hxx @@ -0,0 +1,67 @@ +/* -*- 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 . + */ +#pragma once + +#include <osl/conditn.hxx> +#include <com/sun/star/frame/XSynchronousFrameLoader.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase.hxx> + +namespace com::sun::star::uno { class XComponentContext; } + +namespace chart +{ + +class ChartFrameLoader : public ::cppu::WeakImplHelper< + css::frame::XSynchronousFrameLoader + , css::lang::XServiceInfo + > +{ +private: + css::uno::Reference< css::uno::XComponentContext> m_xCC; + bool m_bCancelRequired; + ::osl::Condition m_oCancelFinished; + +private: + bool impl_checkCancel(); + +public: + ChartFrameLoader() = delete; + + explicit ChartFrameLoader(css::uno::Reference< css::uno::XComponentContext > const & xContext); + virtual ~ChartFrameLoader() override; + + // css::lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // css::frame::XFrameLoader + + virtual sal_Bool SAL_CALL + load( const css::uno::Sequence< css::beans::PropertyValue >& rMediaDescriptor + ,const css::uno::Reference< css::frame::XFrame >& xFrame ) override; + + virtual void SAL_CALL + cancel() override; +}; + +} //namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartModelClone.cxx b/chart2/source/controller/main/ChartModelClone.cxx new file mode 100644 index 0000000000..6e620a32da --- /dev/null +++ b/chart2/source/controller/main/ChartModelClone.cxx @@ -0,0 +1,227 @@ +/* -*- 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 "ChartModelClone.hxx" +#include <ChartModel.hxx> +#include <ChartModelHelper.hxx> +#include <ControllerLockGuard.hxx> +#include <DataSource.hxx> +#include <DataSourceHelper.hxx> + +#include <com/sun/star/chart2/XAnyDescriptionAccess.hpp> +#include <com/sun/star/chart2/XInternalDataProvider.hpp> +#include <com/sun/star/util/XCloneable.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/chart2/XTitled.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/chart2/data/XLabeledDataSequence.hpp> + +#include <comphelper/property.hxx> +#include <comphelper/diagnose_ex.hxx> + +namespace chart +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::frame::XModel; + using ::com::sun::star::util::XCloneable; + using ::com::sun::star::chart2::XChartDocument; + using ::com::sun::star::chart2::XInternalDataProvider; + using ::com::sun::star::chart2::XAnyDescriptionAccess; + using ::com::sun::star::view::XSelectionSupplier; + using ::com::sun::star::chart2::data::XLabeledDataSequence; + + // = helper + namespace + { + rtl::Reference<::chart::ChartModel> lcl_cloneModel( const rtl::Reference<::chart::ChartModel> & xModel ) + { + try + { + return new ChartModel(*xModel); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + return nullptr; + } + + } + + // = ChartModelClone + ChartModelClone::ChartModelClone( const rtl::Reference<::chart::ChartModel>& i_model, const ModelFacet i_facet ) + { + m_xModelClone = lcl_cloneModel( i_model ); + + try + { + if ( i_facet == E_MODEL_WITH_DATA ) + { + ENSURE_OR_THROW( m_xModelClone && m_xModelClone->hasInternalDataProvider(), "invalid chart model" ); + + const Reference< XCloneable > xCloneable( m_xModelClone->getDataProvider(), UNO_QUERY_THROW ); + m_xDataClone.set( xCloneable->createClone(), UNO_QUERY_THROW ); + } + + if ( i_facet == E_MODEL_WITH_SELECTION ) + { + const Reference< XSelectionSupplier > xSelSupp( m_xModelClone->getCurrentController(), UNO_QUERY_THROW ); + m_aSelection = xSelSupp->getSelection(); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + } + + ChartModelClone::~ChartModelClone() + { + if ( !impl_isDisposed() ) + dispose(); + } + + void ChartModelClone::dispose() + { + if ( impl_isDisposed() ) + return; + + m_xModelClone.clear(); + m_xDataClone.clear(); + m_aSelection.clear(); + } + + ModelFacet ChartModelClone::getFacet() const + { + if ( m_aSelection.hasValue() ) + return E_MODEL_WITH_SELECTION; + if ( m_xDataClone.is() ) + return E_MODEL_WITH_DATA; + return E_MODEL; + } + + void ChartModelClone::applyToModel( const rtl::Reference<::chart::ChartModel>& i_model ) const + { + applyModelContentToModel( i_model, m_xModelClone, m_xDataClone ); + + if ( m_aSelection.hasValue() ) + { + try + { + Reference< XSelectionSupplier > xCurrentSelectionSuppl( i_model->getCurrentController(), UNO_QUERY_THROW ); + xCurrentSelectionSuppl->select( m_aSelection ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + } + } + + namespace + { + void ImplApplyDataToModel( const Reference< XModel >& i_model, const Reference< XInternalDataProvider > & i_data ) + { + Reference< XChartDocument > xDoc( i_model, UNO_QUERY ); + OSL_ASSERT( xDoc.is() && xDoc->hasInternalDataProvider() ); + + // copy data from stored internal data provider + if( xDoc.is() && xDoc->hasInternalDataProvider()) + { + Reference< XAnyDescriptionAccess > xCurrentData( xDoc->getDataProvider(), UNO_QUERY ); + Reference< XAnyDescriptionAccess > xSavedData( i_data, UNO_QUERY ); + if ( xCurrentData.is() && xSavedData.is() ) + { + xCurrentData->setData( xSavedData->getData() ); + xCurrentData->setAnyRowDescriptions( xSavedData->getAnyRowDescriptions()); + xCurrentData->setAnyColumnDescriptions( xSavedData->getAnyColumnDescriptions()); + } + } + } + } + + void ChartModelClone::applyModelContentToModel( const rtl::Reference<::chart::ChartModel>& i_model, + const rtl::Reference<::chart::ChartModel>& i_modelToCopyFrom, + const Reference< XInternalDataProvider >& i_data ) + { + ENSURE_OR_RETURN_VOID( i_model.is(), "ChartModelElement::applyModelContentToModel: invalid source model!" ); + ENSURE_OR_RETURN_VOID( i_modelToCopyFrom.is(), "ChartModelElement::applyModelContentToModel: invalid source model!" ); + try + { + // locked controllers of destination + ControllerLockGuardUNO aLockedControllers( i_model ); + + // propagate the correct flag for plotting of hidden values to the data provider and all used sequences + ChartModelHelper::setIncludeHiddenCells(ChartModelHelper::isIncludeHiddenCells( i_modelToCopyFrom ), *i_model); + + // diagram + i_model->setFirstDiagram( i_modelToCopyFrom->getFirstDiagram() ); + + // main title + i_model->setTitleObject( i_modelToCopyFrom->getTitleObject() ); + + // page background + ::comphelper::copyProperties( + i_modelToCopyFrom->getPageBackground(), + i_model->getPageBackground() ); + + // apply data (not applied in standard Undo) + if ( i_data.is() ) + ImplApplyDataToModel( i_model, i_data ); + + // register all sequences at the internal data provider to get adapted + // indexes when columns are added/removed + if ( i_model->hasInternalDataProvider() ) + { + Reference< XInternalDataProvider > xNewDataProvider( i_model->getDataProvider(), UNO_QUERY ); + rtl::Reference< DataSource > xUsedData = DataSourceHelper::getUsedData( *i_model ); + if ( xUsedData.is() && xNewDataProvider.is() ) + { + const Sequence< Reference< XLabeledDataSequence > > aData( xUsedData->getDataSequences() ); + for( Reference< XLabeledDataSequence > const & labeledDataSeq : aData ) + { + xNewDataProvider->registerDataSequenceForChanges( labeledDataSeq->getValues() ); + xNewDataProvider->registerDataSequenceForChanges( labeledDataSeq->getLabel() ); + } + } + } + + // restore modify status + if ( !i_modelToCopyFrom->isModified() ) + { + i_model->setModified( false ); + } + // \-- locked controllers of destination + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + } + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartModelClone.hxx b/chart2/source/controller/main/ChartModelClone.hxx new file mode 100644 index 0000000000..41cf7fc109 --- /dev/null +++ b/chart2/source/controller/main/ChartModelClone.hxx @@ -0,0 +1,75 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Reference.h> +#include <rtl/ref.hxx> + +namespace com::sun::star::chart2 { class XInternalDataProvider; } +namespace com::sun::star::frame { class XModel; } + +namespace chart +{ +class ChartModel; + + enum ModelFacet + { + E_MODEL, + E_MODEL_WITH_DATA, + E_MODEL_WITH_SELECTION + }; + + class ChartModelClone + { + public: + ChartModelClone( + const rtl::Reference<::chart::ChartModel>& i_model, + const ModelFacet i_facet + ); + + ~ChartModelClone(); + + ChartModelClone(const ChartModelClone&) = delete; + const ChartModelClone& operator=(const ChartModelClone&) = delete; + + ModelFacet getFacet() const; + + void applyToModel( const rtl::Reference<::chart::ChartModel>& i_model ) const; + + static void applyModelContentToModel( + const rtl::Reference<::chart::ChartModel> & i_model, + const rtl::Reference<::chart::ChartModel> & i_modelToCopyFrom, + const css::uno::Reference< css::chart2::XInternalDataProvider > & i_data ); + + void dispose(); + + private: + bool impl_isDisposed() const { return !m_xModelClone.is(); } + + private: + rtl::Reference<::chart::ChartModel> m_xModelClone; + css::uno::Reference< css::chart2::XInternalDataProvider > m_xDataClone; + css::uno::Any m_aSelection; + }; + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartTransferable.cxx b/chart2/source/controller/main/ChartTransferable.cxx new file mode 100644 index 0000000000..2f27902ba1 --- /dev/null +++ b/chart2/source/controller/main/ChartTransferable.cxx @@ -0,0 +1,161 @@ +/* -*- 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 <sal/config.h> + +#include <memory> + +#include "ChartTransferable.hxx" + +#include <sot/exchange.hxx> +#include <sot/storage.hxx> +#include <unotools/streamwrap.hxx> +#include <vcl/graph.hxx> +#include <svl/itempool.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <svx/svditer.hxx> +#include <svx/svdmodel.hxx> +#include <svx/unomodel.hxx> +#include <svx/svdview.hxx> +#include <osl/diagnose.h> + +constexpr sal_uInt32 CHARTTRANSFER_OBJECTTYPE_DRAWMODEL = 1; + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; + +namespace chart +{ + +ChartTransferable::ChartTransferable( + SdrModel& rSdrModel, + SdrObject* pSelectedObj, + bool bDrawing) + : m_bDrawing(bDrawing) +{ + std::unique_ptr<SdrExchangeView> pExchgView(std::make_unique<SdrView>( rSdrModel )); + SdrPageView* pPv = pExchgView->ShowSdrPage( rSdrModel.GetPage( 0 )); + if( pSelectedObj ) + pExchgView->MarkObj( pSelectedObj, pPv ); + else + pExchgView->MarkAllObj( pPv ); + Graphic aGraphic( pExchgView->GetMarkedObjMetaFile(true)); + m_xMetaFileGraphic.set( aGraphic.GetXGraphic()); + if ( m_bDrawing ) + { + m_xMarkedObjModel = pExchgView->CreateMarkedObjModel(); + } +} + +ChartTransferable::~ChartTransferable() +{} + +void ChartTransferable::AddSupportedFormats() +{ + if ( m_bDrawing ) + { + AddFormat( SotClipboardFormatId::DRAWING ); + } + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); +} + +bool ChartTransferable::GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ ) +{ + SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor ); + bool bResult = false; + + if( HasFormat( nFormat )) + { + if ( nFormat == SotClipboardFormatId::DRAWING ) + { + bResult = SetObject(m_xMarkedObjModel.get(), CHARTTRANSFER_OBJECTTYPE_DRAWMODEL, rFlavor); + } + else if ( nFormat == SotClipboardFormatId::GDIMETAFILE ) + { + Graphic aGraphic( m_xMetaFileGraphic ); + bResult = SetGDIMetaFile( aGraphic.GetGDIMetaFile() ); + } + else if( nFormat == SotClipboardFormatId::BITMAP ) + { + Graphic aGraphic( m_xMetaFileGraphic ); + bResult = SetBitmapEx( aGraphic.GetBitmapEx(), rFlavor ); + } + } + + return bResult; +} + +bool ChartTransferable::WriteObject( tools::SvRef<SotTempStream>& rxOStm, void* pUserObject, sal_uInt32 nUserObjectId, + const datatransfer::DataFlavor& /* rFlavor */ ) +{ + // called from SetObject, put data into stream + + bool bRet = false; + switch ( nUserObjectId ) + { + case CHARTTRANSFER_OBJECTTYPE_DRAWMODEL: + { + SdrModel* pMarkedObjModel = static_cast< SdrModel* >( pUserObject ); + if ( pMarkedObjModel ) + { + rxOStm->SetBufferSize( 0xff00 ); + + // for the changed pool defaults from drawing layer pool set those + // attributes as hard attributes to preserve them for saving + const SfxItemPool& rItemPool = pMarkedObjModel->GetItemPool(); + const SvxFontHeightItem& rDefaultFontHeight = rItemPool.GetDefaultItem( EE_CHAR_FONTHEIGHT ); + sal_uInt16 nCount = pMarkedObjModel->GetPageCount(); + for ( sal_uInt16 i = 0; i < nCount; ++i ) + { + const SdrPage* pPage = pMarkedObjModel->GetPage( i ); + SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups ); + while ( aIter.IsMore() ) + { + SdrObject* pObj = aIter.Next(); + const SvxFontHeightItem& rItem = pObj->GetMergedItem( EE_CHAR_FONTHEIGHT ); + if ( rItem.GetHeight() == rDefaultFontHeight.GetHeight() ) + { + pObj->SetMergedItem( rDefaultFontHeight ); + } + } + } + + Reference< io::XOutputStream > xDocOut( new utl::OOutputStreamWrapper( *rxOStm ) ); + SvxDrawingLayerExport( pMarkedObjModel, xDocOut ); + + bRet = ( rxOStm->GetError() == ERRCODE_NONE ); + } + } + break; + default: + { + OSL_FAIL( "ChartTransferable::WriteObject: unknown object id" ); + } + break; + } + return bRet; +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartTransferable.hxx b/chart2/source/controller/main/ChartTransferable.hxx new file mode 100644 index 0000000000..fd782864bf --- /dev/null +++ b/chart2/source/controller/main/ChartTransferable.hxx @@ -0,0 +1,60 @@ +/* -*- 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 . + */ +#pragma once + +#include <vcl/transfer.hxx> + +namespace com::sun::star { + namespace graphic { + class XGraphic; + } +} + +class SdrModel; +class SdrObject; + +namespace chart +{ + +class ChartTransferable : public TransferableHelper +{ +public: + explicit ChartTransferable( + SdrModel& rSdrModel, + SdrObject* pSelectedObj, + bool bDrawing ); + virtual ~ChartTransferable() override; + +protected: + + // implementation of TransferableHelper methods + virtual void AddSupportedFormats() override; + virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override; + virtual bool WriteObject( tools::SvRef<SotTempStream>& rxOStm, void* pUserObject, sal_uInt32 nUserObjectId, + const css::datatransfer::DataFlavor& rFlavor ) override; + +private: + css::uno::Reference< css::graphic::XGraphic > m_xMetaFileGraphic; + std::unique_ptr<SdrModel> m_xMarkedObjModel; + bool m_bDrawing; +}; + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ChartWindow.cxx b/chart2/source/controller/main/ChartWindow.cxx new file mode 100644 index 0000000000..60ab7eb0ed --- /dev/null +++ b/chart2/source/controller/main/ChartWindow.cxx @@ -0,0 +1,376 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_wasm_strip.h> +#include <ChartWindow.hxx> +#include <ChartController.hxx> +#include <helpids.h> +#include <uiobject.hxx> + +#include <vcl/help.hxx> +#include <vcl/settings.hxx> + +#include <sfx2/ipclient.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/lokhelper.hxx> +#include <comphelper/lok.hxx> + +using namespace ::com::sun::star; + +namespace +{ +::tools::Rectangle lcl_AWTRectToVCLRect( const css::awt::Rectangle & rAWTRect ) +{ + ::tools::Rectangle aResult(rAWTRect.X, rAWTRect.Y); + aResult.setWidth( rAWTRect.Width ); + aResult.setHeight( rAWTRect.Height ); + return aResult; +} +} // anonymous namespace + +namespace chart +{ + +ChartWindow::ChartWindow( ChartController* pController, vcl::Window* pParent, WinBits nStyle ) + : Window(pParent, nStyle) + , m_pWindowController( pController ) + , m_bInPaint(false) + , m_pViewShellWindow( nullptr ) +{ + set_id("chart_window"); + SetHelpId( HID_SCH_WIN_DOCUMENT ); + SetMapMode( MapMode(MapUnit::Map100thMM) ); + adjustHighContrastMode(); + // chart does not depend on exact pixel painting => enable antialiased drawing + GetOutDev()->SetAntialiasing( AntialiasingFlags::Enable | GetOutDev()->GetAntialiasing() ); + EnableRTL( false ); + if( pParent ) + pParent->EnableRTL( false );// #i96215# necessary for a correct position of the context menu in rtl mode +} + +ChartWindow::~ChartWindow() +{ + disposeOnce(); +} + +void ChartWindow::dispose() +{ + m_pWindowController = nullptr; + m_pViewShellWindow.clear(); + vcl::Window::dispose(); +} + +void ChartWindow::PrePaint(vcl::RenderContext& ) +{ + // forward VCLs PrePaint window event to DrawingLayer + if (m_pWindowController) + { + m_pWindowController->PrePaint(); + } +} + +void ChartWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + if (comphelper::LibreOfficeKit::isActive() && !rRenderContext.IsVirtual()) + return; + + m_bInPaint = true; + if (m_pWindowController) + { + m_pWindowController->execute_Paint(rRenderContext, rRect); + } + else + { + Window::Paint(rRenderContext, rRect); + } + m_bInPaint = false; +} + +void ChartWindow::MouseButtonDown(const MouseEvent& rMEvt) +{ + if( m_pWindowController ) + m_pWindowController->execute_MouseButtonDown(rMEvt); + else + Window::MouseButtonDown(rMEvt); +} + +void ChartWindow::MouseMove( const MouseEvent& rMEvt ) +{ + if( m_pWindowController ) + m_pWindowController->execute_MouseMove( rMEvt ); + else + Window::MouseMove( rMEvt ); +} + +void ChartWindow::Tracking( const TrackingEvent& rTEvt ) +{ + if( !m_pWindowController ) + Window::Tracking( rTEvt ); +} + +void ChartWindow::MouseButtonUp( const MouseEvent& rMEvt ) +{ + if( m_pWindowController ) + m_pWindowController->execute_MouseButtonUp( rMEvt ); + else + Window::MouseButtonUp( rMEvt ); +} + +void ChartWindow::Resize() +{ + if( m_pWindowController ) + m_pWindowController->execute_Resize(); + else + Window::Resize(); +} + +void ChartWindow::Activate() +{ + if( !m_pWindowController ) + Window::Activate(); +} +void ChartWindow::Deactivate() +{ + if( !m_pWindowController ) + Window::Deactivate(); +} +void ChartWindow::GetFocus() +{ + if( !m_pWindowController ) + Window::GetFocus(); +} +void ChartWindow::LoseFocus() +{ + if( !m_pWindowController ) + Window::LoseFocus(); +} + +void ChartWindow::Command( const CommandEvent& rCEvt ) +{ + if( m_pWindowController ) + m_pWindowController->execute_Command( rCEvt ); + else + Window::Command( rCEvt ); +} + +void ChartWindow::KeyInput( const KeyEvent& rKEvt ) +{ + if( m_pWindowController ) + { + if( !m_pWindowController->execute_KeyInput(rKEvt) ) + Window::KeyInput(rKEvt); + } + else + Window::KeyInput( rKEvt ); +} + +uno::Reference< css::accessibility::XAccessible > ChartWindow::CreateAccessible() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if( m_pWindowController ) + return m_pWindowController->CreateAccessible(); + else + return Window::CreateAccessible(); +#else + return uno::Reference< css::accessibility::XAccessible >(); +#endif +} + +void ChartWindow::DataChanged( const DataChangedEvent& rDCEvt ) +{ + vcl::Window::DataChanged( rDCEvt ); + + if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) + { + adjustHighContrastMode(); + } +} + +void ChartWindow::RequestHelp( const HelpEvent& rHEvt ) +{ + bool bHelpHandled = false; + if( ( rHEvt.GetMode() & HelpEventMode::QUICK ) && + m_pWindowController ) + { + Point aLogicHitPos = PixelToLogic( GetPointerPosPixel()); + OUString aQuickHelpText; + awt::Rectangle aHelpRect; + bool bIsBalloonHelp( Help::IsBalloonHelpEnabled() ); + bHelpHandled = m_pWindowController->requestQuickHelp( aLogicHitPos, bIsBalloonHelp, aQuickHelpText, aHelpRect ); + + if( bHelpHandled ) + { + tools::Rectangle aPixelRect(LogicToPixel(lcl_AWTRectToVCLRect(aHelpRect))); + tools::Rectangle aScreenRect(OutputToScreenPixel(aPixelRect.TopLeft()), + OutputToScreenPixel(aPixelRect.BottomRight())); + + if( bIsBalloonHelp ) + Help::ShowBalloon(this, rHEvt.GetMousePosPixel(), aScreenRect, aQuickHelpText); + else + Help::ShowQuickHelp(this, aScreenRect, aQuickHelpText); + } + } + + if( !bHelpHandled ) + vcl::Window::RequestHelp( rHEvt ); +} + +void ChartWindow::adjustHighContrastMode() +{ + static const DrawModeFlags nContrastMode = + DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | + DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient; + + bool bUseContrast = GetSettings().GetStyleSettings().GetHighContrastMode(); + GetOutDev()->SetDrawMode( bUseContrast ? nContrastMode : DrawModeFlags::Default ); +} + +void ChartWindow::ForceInvalidate() +{ + vcl::Window::Invalidate(); +} +void ChartWindow::ImplInvalidate( const vcl::Region* rRegion, InvalidateFlags nFlags ) +{ + if( m_bInPaint ) // #i101928# superfluous paint calls while entering and editing charts" + return; + vcl::Window::ImplInvalidate( rRegion, nFlags ); +} + +void ChartWindow::LogicInvalidate(const tools::Rectangle* pRectangle) +{ + SfxViewShell* pCurrentShell = SfxViewShell::Current(); + if ( nullptr == pCurrentShell ) + return; + tools::Rectangle aResultRectangle; + if (!pRectangle) + { + // we have to invalidate the whole chart area not the whole document + aResultRectangle = GetBoundingBox(); + } + else + { + tools::Rectangle aRectangle(*pRectangle); + // When dragging shapes the map mode is disabled. + if (IsMapModeEnabled()) + { + if (GetMapMode().GetMapUnit() == MapUnit::Map100thMM) + { + aRectangle = o3tl::convert(aRectangle, o3tl::Length::mm100, o3tl::Length::twip); + } + } + else + { + aRectangle = PixelToLogic(aRectangle, MapMode(MapUnit::MapTwip)); + } + + vcl::Window* pEditWin = GetParentEditWin(); + if (pEditWin) + { + MapMode aCWMapMode = GetMapMode(); + constexpr auto p = o3tl::getConversionMulDiv(o3tl::Length::px, o3tl::Length::twip); + const auto& scaleX = aCWMapMode.GetScaleX(); + const auto& scaleY = aCWMapMode.GetScaleY(); + const auto nXNum = p.first * scaleX.GetDenominator(); + const auto nXDen = p.second * scaleX.GetNumerator(); + const auto nYNum = p.first * scaleY.GetDenominator(); + const auto nYDen = p.second * scaleY.GetNumerator(); + + if (!IsMapModeEnabled()) + { + aRectangle = aRectangle.scale(scaleX.GetDenominator(), scaleX.GetNumerator(), + scaleY.GetDenominator(), scaleY.GetNumerator()); + } + + Point aOffset = this->GetOffsetPixelFrom(*pEditWin).scale(nXNum, nXDen, nYNum, nYDen); + + aRectangle = tools::Rectangle(aRectangle.TopLeft() + aOffset, aRectangle.GetSize()); + } + + aResultRectangle = aRectangle; + } + SfxLokHelper::notifyInvalidation(pCurrentShell, &aResultRectangle); +} + +FactoryFunction ChartWindow::GetUITestFactory() const +{ + return ChartWindowUIObject::create; +} + +ChartController* ChartWindow::GetController() +{ + return m_pWindowController; +} + +vcl::Window* ChartWindow::GetParentEditWin() +{ + if (m_pViewShellWindow) + return m_pViewShellWindow.get(); + + // So, you are thinking, why do not invoke pCurrentShell->GetWindow() ? + // Because in Impress the parent edit win is not view shell window. + SfxViewShell* pCurrentShell = SfxViewShell::Current(); + if( pCurrentShell ) + { + SfxInPlaceClient* pIPClient = pCurrentShell->GetIPClient(); + if (pIPClient) + { + vcl::Window* pRootWin = pIPClient->GetEditWin(); + if(pRootWin && pRootWin->IsAncestorOf(*this)) + { + m_pViewShellWindow = pRootWin; + return m_pViewShellWindow.get(); + } + } + } + return nullptr; +} + +tools::Rectangle ChartWindow::GetBoundingBox() +{ + tools::Rectangle aBBox; + + vcl::Window* pRootWin = GetParentEditWin(); + if (pRootWin) + { + // In all cases, the following code fragment + // returns the chart bounding box in twips. + MapMode aCWMapMode = GetMapMode(); + constexpr auto p = o3tl::getConversionMulDiv(o3tl::Length::px, o3tl::Length::twip); + const auto& scaleX = aCWMapMode.GetScaleX(); + const auto& scaleY = aCWMapMode.GetScaleY(); + const auto nXNum = p.first * scaleX.GetDenominator(); + const auto nXDen = p.second * scaleX.GetNumerator(); + const auto nYNum = p.first * scaleY.GetDenominator(); + const auto nYDen = p.second * scaleY.GetNumerator(); + + Point aOffset = GetOffsetPixelFrom(*pRootWin); + aOffset.setX( o3tl::convert(aOffset.X(), nXNum, nXDen) ); + aOffset.setY( o3tl::convert(aOffset.Y(), nYNum, nYDen) ); + Size aSize = GetSizePixel(); + aSize.setWidth( o3tl::convert(aSize.Width(), nXNum, nXDen) ); + aSize.setHeight( o3tl::convert(aSize.Height(), nYNum, nYDen) ); + aBBox = tools::Rectangle(aOffset, aSize); + } + return aBBox; +} + +} //namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/CommandDispatch.cxx b/chart2/source/controller/main/CommandDispatch.cxx new file mode 100644 index 0000000000..37a56e1512 --- /dev/null +++ b/chart2/source/controller/main/CommandDispatch.cxx @@ -0,0 +1,141 @@ +/* -*- 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 "CommandDispatch.hxx" +#include <com/sun/star/util/URLTransformer.hpp> +#include <comphelper/diagnose_ex.hxx> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace chart +{ + +CommandDispatch::CommandDispatch( + const Reference< uno::XComponentContext > & xContext ) : + m_xContext( xContext ) +{ +} + +CommandDispatch::~CommandDispatch() +{} + +void CommandDispatch::initialize() +{} + +// ____ WeakComponentImplHelperBase ____ +/// is called when this is disposed +void CommandDispatch::disposing(std::unique_lock<std::mutex>& rGuard) +{ + Reference< uno::XInterface > xEventSource(static_cast< cppu::OWeakObject* >( this )); + for( auto& rElement : m_aListeners ) + rElement.second.disposeAndClear( rGuard, xEventSource ); + m_aListeners.clear(); +} + +// ____ XDispatch ____ +void SAL_CALL CommandDispatch::dispatch( const util::URL& /* URL */, const Sequence< beans::PropertyValue >& /* Arguments */ ) +{} + +void SAL_CALL CommandDispatch::addStatusListener( const Reference< frame::XStatusListener >& Control, const util::URL& URL ) +{ + { + std::unique_lock g(m_aMutex); + tListenerMap::iterator aIt( m_aListeners.find( URL.Complete )); + if( aIt == m_aListeners.end()) + { + aIt = m_aListeners.emplace( + std::piecewise_construct, + std::forward_as_tuple(URL.Complete), + std::forward_as_tuple()).first; + } + assert( aIt != m_aListeners.end()); + + aIt->second.addInterface( g, Control ); + } + fireStatusEvent( URL.Complete, Control ); +} + +void SAL_CALL CommandDispatch::removeStatusListener( const Reference< frame::XStatusListener >& Control, const util::URL& URL ) +{ + std::unique_lock g(m_aMutex); + tListenerMap::iterator aIt( m_aListeners.find( URL.Complete )); + if( aIt != m_aListeners.end()) + (*aIt).second.removeInterface( g, Control ); +} + +// ____ XModifyListener ____ +void SAL_CALL CommandDispatch::modified( const lang::EventObject& /* aEvent */ ) +{ + fireAllStatusEvents( nullptr ); +} + +// ____ XEventListener (base of XModifyListener) ____ +void SAL_CALL CommandDispatch::disposing( const lang::EventObject& /* Source */ ) +{} + +void CommandDispatch::fireAllStatusEvents( + const css::uno::Reference< css::frame::XStatusListener > & xSingleListener ) +{ + fireStatusEvent( OUString(), xSingleListener ); +} + +void CommandDispatch::fireStatusEventForURL( + const OUString & rURL, + const uno::Any & rState, + bool bEnabled, + const Reference< frame::XStatusListener > & xSingleListener /* = 0 */) +{ + // prepare event to send + util::URL aURL; + aURL.Complete = rURL; + if( !m_xURLTransformer.is()) + { + m_xURLTransformer.set( util::URLTransformer::create(m_xContext) ); + } + m_xURLTransformer->parseStrict( aURL ); + + frame::FeatureStateEvent aEventToSend( + static_cast< cppu::OWeakObject* >( this ), // Source + aURL, // FeatureURL + OUString(), // FeatureDescriptor + bEnabled, // IsEnabled + false, // Requery + rState // State + ); + + // send event either to single listener or all registered ones + if( xSingleListener.is()) + xSingleListener->statusChanged( aEventToSend ); + else + { + tListenerMap::iterator aIt( m_aListeners.find( aURL.Complete )); + if( aIt != m_aListeners.end()) + { + std::unique_lock g(m_aMutex); + aIt->second.notifyEach(g, &css::frame::XStatusListener::statusChanged, aEventToSend); + } + } +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/CommandDispatch.hxx b/chart2/source/controller/main/CommandDispatch.hxx new file mode 100644 index 0000000000..41171926b6 --- /dev/null +++ b/chart2/source/controller/main/CommandDispatch.hxx @@ -0,0 +1,130 @@ +/* -*- 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 . + */ +#pragma once + +#include <comphelper/compbase.hxx> +#include <comphelper/interfacecontainer4.hxx> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/util/XModifyListener.hpp> + +#include <map> +#include <memory> + +namespace com::sun::star::uno { class XComponentContext; } +namespace com::sun::star::util { class XURLTransformer; } + +namespace chart +{ + +namespace impl +{ +typedef ::comphelper::WeakComponentImplHelper< + css::frame::XDispatch, + css::util::XModifyListener > + CommandDispatch_Base; +} + +/** This is the base class for an XDispatch. + */ +class CommandDispatch : public impl::CommandDispatch_Base +{ +public: + explicit CommandDispatch( const css::uno::Reference< css::uno::XComponentContext > & xContext ); + virtual ~CommandDispatch() override; + + // late initialisation, especially for adding as listener + virtual void initialize(); + +protected: + /** sends a status event for a specific command to all registered listeners + or only the one given when set. + + This method should be overridden. The implementation should call + fireStatusEventForURL and pass the xSingleListener argument to this + method unchanged. + + @param rURL + If empty, all available status events must be fired, otherwise only + the one for the given command. + + @param xSingleListener + If set, the event is only sent to this listener rather than to all + registered ones. Whenever a listener adds itself, this method is + called with this parameter set to give an initial state. + */ + virtual void fireStatusEvent( + const OUString & rURL, + const css::uno::Reference< css::frame::XStatusListener > & xSingleListener ) = 0; + + /** calls fireStatusEvent( OUString, xSingleListener ) + */ + void fireAllStatusEvents( + const css::uno::Reference< css::frame::XStatusListener > & xSingleListener ); + + /** sends a status event for a specific command to all registered listeners + or only the one given when set. + + @param xSingleListener + If set, the event is only sent to this listener rather than to all + registered ones. Whenever a listener adds itself, this method is + called with this parameter set to give an initial state. + */ + void fireStatusEventForURL( + const OUString & rURL, + const css::uno::Any & rState, + bool bEnabled, + const css::uno::Reference< css::frame::XStatusListener > & xSingleListener ); + + // ____ XDispatch ____ + virtual void SAL_CALL dispatch( + const css::util::URL& URL, + const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + virtual void SAL_CALL addStatusListener( + const css::uno::Reference< css::frame::XStatusListener >& Control, + const css::util::URL& URL ) override; + virtual void SAL_CALL removeStatusListener( + const css::uno::Reference< css::frame::XStatusListener >& Control, + const css::util::URL& URL ) override; + + // ____ WeakComponentImplHelperBase ____ + /// is called when this is disposed + virtual void disposing(std::unique_lock<std::mutex>& rGuard) override; + + // ____ XModifyListener ____ + virtual void SAL_CALL modified( + const css::lang::EventObject& aEvent ) override; + + // ____ XEventListener (base of XModifyListener) ____ + virtual void SAL_CALL disposing( + const css::lang::EventObject& Source ) override; + +private: + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::util::XURLTransformer > m_xURLTransformer; + + typedef std::map< OUString, ::comphelper::OInterfaceContainerHelper4<css::frame::XStatusListener> > + tListenerMap; + + tListenerMap m_aListeners; + +}; + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/CommandDispatchContainer.cxx b/chart2/source/controller/main/CommandDispatchContainer.cxx new file mode 100644 index 0000000000..c16a0e45be --- /dev/null +++ b/chart2/source/controller/main/CommandDispatchContainer.cxx @@ -0,0 +1,201 @@ +/* -*- 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 <CommandDispatchContainer.hxx> +#include "UndoCommandDispatch.hxx" +#include "StatusBarCommandDispatch.hxx" +#include <DisposeHelper.hxx> +#include "DrawCommandDispatch.hxx" +#include "ShapeController.hxx" +#include <ChartModel.hxx> + +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <osl/diagnose.h> +#include <rtl/ref.hxx> + +#include <o3tl/sorted_vector.hxx> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace chart +{ + +CommandDispatchContainer::CommandDispatchContainer( + const Reference< uno::XComponentContext > & xContext ) + :m_xContext( xContext ) + ,m_pDrawCommandDispatch( nullptr ) + ,m_pShapeController( nullptr ) +{ +} + +void CommandDispatchContainer::setModel( + const rtl::Reference<::chart::ChartModel> & xModel ) +{ + // remove all existing dispatcher that base on the old model + m_aCachedDispatches.clear(); + DisposeHelper::DisposeAllElements( m_aToBeDisposedDispatches ); + m_aToBeDisposedDispatches.clear(); + m_xModel = xModel.get(); +} + +void CommandDispatchContainer::setChartDispatch( + const Reference< frame::XDispatch >& rChartDispatch, + o3tl::sorted_vector< OUString > && rChartCommands ) +{ + OSL_ENSURE(rChartDispatch.is(),"Invalid fall back dispatcher!"); + m_xChartDispatcher.set( rChartDispatch ); + m_aChartCommands = std::move(rChartCommands); + m_aToBeDisposedDispatches.push_back( m_xChartDispatcher ); +} + +Reference< frame::XDispatch > CommandDispatchContainer::getDispatchForURL( + const util::URL & rURL ) +{ + static const o3tl::sorted_vector< OUString > s_aContainerDocumentCommands { + "AddDirect", "NewDoc", "Open", + "Save", "SaveAs", "SendMail", + "EditDoc", "ExportDirectToPDF", "PrintDefault"}; + + Reference< frame::XDispatch > xResult; + tDispatchMap::const_iterator aIt( m_aCachedDispatches.find( rURL.Complete )); + if( aIt != m_aCachedDispatches.end()) + { + xResult.set( (*aIt).second ); + } + else + { + rtl::Reference< ::chart::ChartModel > xModel( m_xModel ); + + if( xModel.is() && ( rURL.Path == "Undo" || rURL.Path == "Redo" || + rURL.Path == "GetUndoStrings" || rURL.Path == "GetRedoStrings" ) ) + { + rtl::Reference<CommandDispatch> pDispatch = new UndoCommandDispatch( m_xContext, xModel ); + xResult.set( pDispatch ); + pDispatch->initialize(); + m_aCachedDispatches[ ".uno:Undo" ].set( xResult ); + m_aCachedDispatches[ ".uno:Redo" ].set( xResult ); + m_aCachedDispatches[ ".uno:GetUndoStrings" ].set( xResult ); + m_aCachedDispatches[ ".uno:GetRedoStrings" ].set( xResult ); + m_aToBeDisposedDispatches.push_back( xResult ); + } + else if( xModel.is() && ( rURL.Path == "Context" || rURL.Path == "ModifiedStatus" ) ) + { + Reference< view::XSelectionSupplier > xSelSupp( xModel->getCurrentController(), uno::UNO_QUERY ); + rtl::Reference<CommandDispatch> pDispatch = new StatusBarCommandDispatch( m_xContext, xModel, xSelSupp ); + xResult.set( pDispatch ); + pDispatch->initialize(); + m_aCachedDispatches[ ".uno:Context" ].set( xResult ); + m_aCachedDispatches[ ".uno:ModifiedStatus" ].set( xResult ); + m_aToBeDisposedDispatches.push_back( xResult ); + } + else if( xModel.is() && + (s_aContainerDocumentCommands.find( rURL.Path ) != s_aContainerDocumentCommands.end()) ) + { + xResult.set( getContainerDispatchForURL( xModel->getCurrentController(), rURL )); + // ToDo: can those dispatches be cached? + m_aCachedDispatches[ rURL.Complete ].set( xResult ); + } + else if( m_xChartDispatcher.is() && + (m_aChartCommands.find( rURL.Path ) != m_aChartCommands.end()) ) + { + xResult.set( m_xChartDispatcher ); + m_aCachedDispatches[ rURL.Complete ].set( xResult ); + } + // #i12587# support for shapes in chart + // Note, that the chart dispatcher must be queried first, because + // the chart dispatcher is the default dispatcher for all context + // sensitive commands. + else if ( m_pDrawCommandDispatch && m_pDrawCommandDispatch->isFeatureSupported( rURL.Complete ) ) + { + xResult.set( m_pDrawCommandDispatch ); + m_aCachedDispatches[ rURL.Complete ].set( xResult ); + } + else if ( m_pShapeController && m_pShapeController->isFeatureSupported( rURL.Complete ) ) + { + xResult.set( m_pShapeController ); + m_aCachedDispatches[ rURL.Complete ].set( xResult ); + } + } + + return xResult; +} + +Sequence< Reference< frame::XDispatch > > CommandDispatchContainer::getDispatchesForURLs( + const Sequence< frame::DispatchDescriptor > & aDescriptors ) +{ + sal_Int32 nCount = aDescriptors.getLength(); + uno::Sequence< uno::Reference< frame::XDispatch > > aRet( nCount ); + auto aRetRange = asNonConstRange(aRet); + + for( sal_Int32 nPos = 0; nPos < nCount; ++nPos ) + { + if ( aDescriptors[ nPos ].FrameName == "_self" ) + aRetRange[ nPos ] = getDispatchForURL( aDescriptors[ nPos ].FeatureURL ); + } + return aRet; +} + +void CommandDispatchContainer::DisposeAndClear() +{ + m_aCachedDispatches.clear(); + DisposeHelper::DisposeAllElements( m_aToBeDisposedDispatches ); + m_aToBeDisposedDispatches.clear(); + m_xChartDispatcher.clear(); + m_aChartCommands.clear(); + m_pDrawCommandDispatch = nullptr; + m_pShapeController = nullptr; +} + +Reference< frame::XDispatch > CommandDispatchContainer::getContainerDispatchForURL( + const Reference< frame::XController > & xChartController, + const util::URL & rURL ) +{ + Reference< frame::XDispatch > xResult; + if( xChartController.is()) + { + Reference< frame::XFrame > xFrame( xChartController->getFrame()); + if( xFrame.is()) + { + Reference< frame::XDispatchProvider > xDispProv( xFrame->getCreator(), uno::UNO_QUERY ); + if( xDispProv.is()) + xResult.set( xDispProv->queryDispatch( rURL, "_self", 0 )); + } + } + return xResult; +} + +void CommandDispatchContainer::setDrawCommandDispatch( DrawCommandDispatch* pDispatch ) +{ + m_pDrawCommandDispatch = pDispatch; + m_aToBeDisposedDispatches.emplace_back( pDispatch ); +} + +void CommandDispatchContainer::setShapeController( ShapeController* pController ) +{ + m_pShapeController = pController; + m_aToBeDisposedDispatches.emplace_back( pController ); +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ControllerCommandDispatch.cxx b/chart2/source/controller/main/ControllerCommandDispatch.cxx new file mode 100644 index 0000000000..5317c1c47c --- /dev/null +++ b/chart2/source/controller/main/ControllerCommandDispatch.cxx @@ -0,0 +1,844 @@ +/* -*- 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 "ControllerCommandDispatch.hxx" +#include <ChartModelHelper.hxx> +#include <ChartModel.hxx> +#include <DiagramHelper.hxx> +#include <Diagram.hxx> +#include <Axis.hxx> +#include <AxisHelper.hxx> +#include <TitleHelper.hxx> +#include <LegendHelper.hxx> +#include <ObjectIdentifier.hxx> +#include <ChartType.hxx> +#include <ChartTypeHelper.hxx> +#include <ChartController.hxx> +#include <RegressionCurveHelper.hxx> +#include <DataSeries.hxx> +#include <DataSeriesHelper.hxx> +#include <StatisticsHelper.hxx> +#include <ReferenceSizeProvider.hxx> +#include "ShapeController.hxx" + +#include <vcl/svapp.hxx> +#include <sal/log.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <comphelper/lok.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/objsh.hxx> + +#include <com/sun/star/chart2/XRegressionCurve.hpp> +#include <com/sun/star/chart2/XDataProviderAccess.hpp> + +// only needed until #i68864# is fixed +#include <com/sun/star/frame/XLayoutManager.hpp> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace +{ +bool lcl_isStatusBarVisible( const Reference< frame::XController > & xController ) +{ + bool bIsStatusBarVisible = false; + // Status-Bar visible, workaround: this should not be necessary. @todo: + // remove when Issue #i68864# is fixed + if( xController.is()) + { + Reference< beans::XPropertySet > xPropSet( xController->getFrame(), uno::UNO_QUERY ); + if( xPropSet.is() ) + { + uno::Reference< css::frame::XLayoutManager > xLayoutManager; + xPropSet->getPropertyValue( "LayoutManager" ) >>= xLayoutManager; + if ( xLayoutManager.is() ) + bIsStatusBarVisible = xLayoutManager->isElementVisible( "private:resource/statusbar/statusbar" ); + } + } + return bIsStatusBarVisible; +} + +} // anonymous namespace + +namespace chart +{ + +namespace impl +{ + +/// Constants for moving the series. +namespace { + bool const MOVE_SERIES_FORWARD = true; + bool const MOVE_SERIES_BACKWARD = false; +} + +/** Represents the current state of the controller (needed for issue 63017). + + You can set the state by calling update(). After this call the state is + preserved in this class until the next call to update(). + + This is useful, not to say necessary, for enabling and disabling of menu + entries (e.g. format>arrangement). As the status requests are sent very + frequently it would be impossible, from a performance point of view, to + query the current status every time directly at the model. So this class + serves as a cache for the state. +*/ +struct ControllerState +{ + ControllerState(); + + void update( const Reference< frame::XController > & xController, + const rtl::Reference<::chart::ChartModel> & xModel ); + + // -- State variables ------- + bool bHasSelectedObject; + bool bIsPositionableObject; + bool bIsTextObject; + bool bIsDeleteableObjectSelected; + bool bIsFormateableObjectSelected; + + // May the selected series be moved forward or backward (cf + // format>arrangement). + bool bMayMoveSeriesForward; + bool bMayMoveSeriesBackward; + + // trendlines + bool bMayAddMenuTrendline; + bool bMayAddTrendline; + bool bMayAddTrendlineEquation; + bool bMayAddR2Value; + bool bMayAddMeanValue; + bool bMayAddXErrorBars; + bool bMayAddYErrorBars; + + bool bMayDeleteTrendline; + bool bMayDeleteTrendlineEquation; + bool bMayDeleteR2Value; + bool bMayDeleteMeanValue; + bool bMayDeleteXErrorBars; + bool bMayDeleteYErrorBars; + + bool bMayFormatTrendline; + bool bMayFormatTrendlineEquation; + bool bMayFormatMeanValue; + bool bMayFormatXErrorBars; + bool bMayFormatYErrorBars; +}; + +ControllerState::ControllerState() : + bHasSelectedObject( false ), + bIsPositionableObject( false ), + bIsTextObject(false), + bIsDeleteableObjectSelected(false), + bIsFormateableObjectSelected(false), + bMayMoveSeriesForward( false ), + bMayMoveSeriesBackward( false ), + bMayAddMenuTrendline( false ), + bMayAddTrendline( false ), + bMayAddTrendlineEquation( false ), + bMayAddR2Value( false ), + bMayAddMeanValue( false ), + bMayAddXErrorBars( false ), + bMayAddYErrorBars( false ), + bMayDeleteTrendline( false ), + bMayDeleteTrendlineEquation( false ), + bMayDeleteR2Value( false ), + bMayDeleteMeanValue( false ), + bMayDeleteXErrorBars( false ), + bMayDeleteYErrorBars( false ), + bMayFormatTrendline( false ), + bMayFormatTrendlineEquation( false ), + bMayFormatMeanValue( false ), + bMayFormatXErrorBars( false ), + bMayFormatYErrorBars( false ) +{} + +void ControllerState::update( + const Reference< frame::XController > & xController, + const rtl::Reference<::chart::ChartModel> & xModel ) +{ + Reference< view::XSelectionSupplier > xSelectionSupplier( + xController, uno::UNO_QUERY ); + + // Update ControllerState variables. + if( !xSelectionSupplier.is()) + return; + + uno::Any aSelObj( xSelectionSupplier->getSelection() ); + ObjectIdentifier aSelOID( aSelObj ); + OUString aSelObjCID( aSelOID.getObjectCID() ); + + bHasSelectedObject = aSelOID.isValid(); + + ObjectType aObjectType(ObjectIdentifier::getObjectType( aSelObjCID )); + + bIsPositionableObject = (aObjectType != OBJECTTYPE_DATA_POINT) && aSelOID.isDragableObject(); + bIsTextObject = aObjectType == OBJECTTYPE_TITLE; + + rtl::Reference< Diagram > xDiagram = xModel->getFirstChartDiagram(); + bIsFormateableObjectSelected = bHasSelectedObject && aSelOID.isAutoGeneratedObject(); + if( aObjectType==OBJECTTYPE_DIAGRAM || aObjectType==OBJECTTYPE_DIAGRAM_WALL || aObjectType==OBJECTTYPE_DIAGRAM_FLOOR ) + bIsFormateableObjectSelected = xDiagram->isSupportingFloorAndWall(); + + rtl::Reference< DataSeries > xGivenDataSeries = + ObjectIdentifier::getDataSeriesForCID( + aSelObjCID, xModel ); + + bIsDeleteableObjectSelected = ChartController::isObjectDeleteable( aSelObj ); + + bMayMoveSeriesForward = (aObjectType!=OBJECTTYPE_DATA_POINT) && xDiagram && xDiagram->isSeriesMoveable( + xGivenDataSeries, + MOVE_SERIES_FORWARD ); + + bMayMoveSeriesBackward = (aObjectType!=OBJECTTYPE_DATA_POINT) && xDiagram && xDiagram->isSeriesMoveable( + xGivenDataSeries, + MOVE_SERIES_BACKWARD ); + + bMayAddMenuTrendline = false; + bMayAddTrendline = false; + bMayAddTrendlineEquation = false; + bMayAddR2Value = false; + bMayAddMeanValue = false; + bMayAddXErrorBars = false; + bMayAddYErrorBars = false; + bMayDeleteTrendline = false; + bMayDeleteTrendlineEquation = false; + bMayDeleteR2Value = false; + bMayDeleteMeanValue = false; + bMayDeleteXErrorBars = false; + bMayDeleteYErrorBars = false; + bMayFormatTrendline = false; + bMayFormatTrendlineEquation = false; + bMayFormatMeanValue = false; + bMayFormatXErrorBars = false; + bMayFormatYErrorBars = false; + if( !bHasSelectedObject ) + return; + + if( xGivenDataSeries.is()) + { + bMayAddMenuTrendline = true; + sal_Int32 nDimensionCount = xDiagram->getDimension(); + rtl::Reference< ::chart::ChartType > xFirstChartType( + DataSeriesHelper::getChartTypeOfSeries( xGivenDataSeries, xDiagram )); + + // trend lines/mean value line + if( (aObjectType == OBJECTTYPE_DATA_SERIES || aObjectType == OBJECTTYPE_DATA_POINT) + && ChartTypeHelper::isSupportingRegressionProperties( xFirstChartType, nDimensionCount )) + { + // Trendline + bMayAddTrendline = true; + + // Mean Value + bMayFormatMeanValue = bMayDeleteMeanValue = RegressionCurveHelper::hasMeanValueLine( xGivenDataSeries ); + bMayAddMeanValue = ! bMayDeleteMeanValue; + } + + // error bars + if( (aObjectType == OBJECTTYPE_DATA_SERIES || aObjectType == OBJECTTYPE_DATA_POINT) + && ChartTypeHelper::isSupportingStatisticProperties( xFirstChartType, nDimensionCount )) + { + bMayFormatXErrorBars = bMayDeleteXErrorBars = StatisticsHelper::hasErrorBars( xGivenDataSeries, false ); + bMayAddXErrorBars = ! bMayDeleteXErrorBars; + + bMayFormatYErrorBars = bMayDeleteYErrorBars = StatisticsHelper::hasErrorBars( xGivenDataSeries ); + bMayAddYErrorBars = ! bMayDeleteYErrorBars; + } + } + + if( aObjectType == OBJECTTYPE_DATA_AVERAGE_LINE ) + bMayFormatMeanValue = true; + + if( aObjectType == OBJECTTYPE_DATA_ERRORS_X) + bMayFormatXErrorBars = true; + + if( aObjectType == OBJECTTYPE_DATA_ERRORS_Y ) + bMayFormatYErrorBars = true; + + if( aObjectType == OBJECTTYPE_DATA_CURVE ) + { + bMayFormatTrendline = true; + bMayDeleteTrendline = true; + uno::Reference< chart2::XRegressionCurve > xRegCurve( + ObjectIdentifier::getObjectPropertySet( aSelObjCID, xModel ), uno::UNO_QUERY ); + + // Trendline Equation + bMayFormatTrendlineEquation = bMayDeleteTrendlineEquation = RegressionCurveHelper::hasEquation( xRegCurve ); + bMayAddTrendlineEquation = !bMayDeleteTrendlineEquation; + bMayAddR2Value = RegressionCurveHelper::MayHaveCorrelationCoefficient( xRegCurve ) && bMayAddTrendlineEquation; + } + else if( aObjectType == OBJECTTYPE_DATA_CURVE_EQUATION ) + { + bMayFormatTrendlineEquation = true; + bool bHasR2Value = false; + bool bMayHaveR2 = true; + try + { + uno::Reference< beans::XPropertySet > xEquationProperties = + ObjectIdentifier::getObjectPropertySet( aSelObjCID, xModel ); + if( xEquationProperties.is() ) + { + xEquationProperties->getPropertyValue( "ShowCorrelationCoefficient" ) >>= bHasR2Value; + xEquationProperties->getPropertyValue( "MayHaveCorrelationCoefficient" ) >>= bMayHaveR2; + } + } + catch(const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } + bMayAddR2Value = !bHasR2Value && bMayHaveR2; + bMayDeleteR2Value = bHasR2Value; + } +} + +/** Represents the current state of the model. + + You can set the state by calling update(). After this call the state is + preserved in this class until the next call to update(). + + This is useful, not to say necessary, for enabling and disabling of menu + entries and toolbar icons. As the status requests are sent very frequently + it would be impossible, from a performance point of view, to query the + current status every time directly at the model. So this class serves as a + cache for the state. + */ +struct ModelState +{ + ModelState(); + + void update( const rtl::Reference<::chart::ChartModel> & xModel ); + + bool HasAnyAxis() const; + bool HasAnyGrid() const; + bool HasAnyTitle() const; + + bool bIsReadOnly; + bool bIsThreeD; + bool bHasOwnData; + bool bHasDataFromPivotTable; + + bool bHasMainTitle; + bool bHasSubTitle; + bool bHasXAxisTitle; + bool bHasYAxisTitle; + bool bHasZAxisTitle; + bool bHasSecondaryXAxisTitle; + bool bHasSecondaryYAxisTitle; + + bool bHasXAxis; + bool bHasYAxis; + bool bHasZAxis; + bool bHasAAxis; + bool bHasBAxis; + + bool bHasMainXGrid; + bool bHasMainYGrid; + bool bHasMainZGrid; + bool bHasHelpXGrid; + bool bHasHelpYGrid; + bool bHasHelpZGrid; + + bool bHasAutoScaledText; + bool bHasLegend; + bool bHasWall; + bool bHasFloor; + + bool bSupportsStatistics; + bool bSupportsAxes; + + bool bDataTable = false; +}; + +ModelState::ModelState() : + bIsReadOnly(true), + bIsThreeD(false), + bHasOwnData(false), + bHasDataFromPivotTable(false), + bHasMainTitle(false), + bHasSubTitle(false), + bHasXAxisTitle(false), + bHasYAxisTitle(false), + bHasZAxisTitle(false), + bHasSecondaryXAxisTitle(false), + bHasSecondaryYAxisTitle(false), + bHasXAxis(false), + bHasYAxis(false), + bHasZAxis(false), + bHasAAxis(false), + bHasBAxis(false), + bHasMainXGrid(false), + bHasMainYGrid(false), + bHasMainZGrid(false), + bHasHelpXGrid(false), + bHasHelpYGrid(false), + bHasHelpZGrid(false), + bHasAutoScaledText(false), + bHasLegend(false), + bHasWall(false), + bHasFloor(false), + bSupportsStatistics(false), + bSupportsAxes(false) +{} + +void ModelState::update( const rtl::Reference<::chart::ChartModel> & xModel ) +{ + rtl::Reference< Diagram > xDiagram = xModel->getFirstChartDiagram(); + + bIsReadOnly = xModel->isReadonly(); + + sal_Int32 nDimensionCount = 0; + if (xDiagram) + nDimensionCount = xDiagram->getDimension(); + + rtl::Reference< ChartType > xFirstChartType; + if (xDiagram) + xFirstChartType = xDiagram->getChartTypeByIndex( 0 ); + bSupportsStatistics = ChartTypeHelper::isSupportingStatisticProperties( xFirstChartType, nDimensionCount ); + bSupportsAxes = ChartTypeHelper::isSupportingMainAxis( xFirstChartType, nDimensionCount, 0 ); + + bIsThreeD = (nDimensionCount == 3); + if (xModel.is()) + { + bHasOwnData = xModel->hasInternalDataProvider(); + bHasDataFromPivotTable = !bHasOwnData && xModel->isDataFromPivotTable(); + } + + bHasMainTitle = TitleHelper::getTitle( TitleHelper::MAIN_TITLE, xModel ).is(); + bHasSubTitle = TitleHelper::getTitle( TitleHelper::SUB_TITLE, xModel ).is(); + bHasXAxisTitle = TitleHelper::getTitle( TitleHelper::X_AXIS_TITLE, xModel ).is(); + bHasYAxisTitle = TitleHelper::getTitle( TitleHelper::Y_AXIS_TITLE, xModel ).is(); + bHasZAxisTitle = TitleHelper::getTitle( TitleHelper::Z_AXIS_TITLE, xModel ).is(); + bHasSecondaryXAxisTitle = TitleHelper::getTitle( TitleHelper::SECONDARY_X_AXIS_TITLE, xModel ).is(); + bHasSecondaryYAxisTitle = TitleHelper::getTitle( TitleHelper::SECONDARY_Y_AXIS_TITLE, xModel ).is(); + + bHasXAxis = bSupportsAxes && AxisHelper::getAxis( 0, true, xDiagram ).is(); + bHasYAxis = bSupportsAxes && AxisHelper::getAxis( 1, true, xDiagram ).is(); + bHasZAxis = bSupportsAxes && AxisHelper::getAxis( 2, true, xDiagram ).is(); + bHasAAxis = bSupportsAxes && AxisHelper::getAxis( 0, false, xDiagram ).is(); + bHasBAxis = bSupportsAxes && AxisHelper::getAxis( 1, false, xDiagram ).is(); + + bHasMainXGrid = bSupportsAxes && AxisHelper::isGridShown( 0, 0, true, xDiagram ); + bHasMainYGrid = bSupportsAxes && AxisHelper::isGridShown( 1, 0, true, xDiagram ); + bHasMainZGrid = bSupportsAxes && AxisHelper::isGridShown( 2, 0, true, xDiagram ); + bHasHelpXGrid = bSupportsAxes && AxisHelper::isGridShown( 0, 0, false, xDiagram ); + bHasHelpYGrid = bSupportsAxes && AxisHelper::isGridShown( 1, 0, false, xDiagram ); + bHasHelpZGrid = bSupportsAxes && AxisHelper::isGridShown( 2, 0, false, xDiagram ); + + bHasAutoScaledText = + (ReferenceSizeProvider::getAutoResizeState( xModel ) == + ReferenceSizeProvider::AUTO_RESIZE_YES); + + bHasLegend = LegendHelper::hasLegend( xDiagram ); + bHasWall = xDiagram && xDiagram->isSupportingFloorAndWall(); + bHasFloor = bHasWall && bIsThreeD; + + bDataTable = xDiagram.is() && xDiagram->getDataTable().is(); +} + +bool ModelState::HasAnyAxis() const +{ + return bHasXAxis || bHasYAxis || bHasZAxis || bHasAAxis || bHasBAxis; +} + +bool ModelState::HasAnyGrid() const +{ + return bHasMainXGrid || bHasMainYGrid || bHasMainZGrid || + bHasHelpXGrid || bHasHelpYGrid || bHasHelpZGrid; +} + +bool ModelState::HasAnyTitle() const +{ + return bHasMainTitle || bHasSubTitle || bHasXAxisTitle || bHasYAxisTitle || bHasZAxisTitle || bHasSecondaryXAxisTitle || bHasSecondaryYAxisTitle; +} + +} // namespace impl + +ControllerCommandDispatch::ControllerCommandDispatch( + const Reference< uno::XComponentContext > & xContext, + ChartController* pController, CommandDispatchContainer* pContainer ) : + impl::ControllerCommandDispatch_Base( xContext ), + m_xChartController( pController ), + m_xSelectionSupplier( Reference< view::XSelectionSupplier >( pController ) ), + m_xDispatch( Reference< frame::XDispatch >( pController ) ), + m_apModelState( new impl::ModelState() ), + m_apControllerState( new impl::ControllerState() ), + m_pDispatchContainer( pContainer ) +{ +} + +ControllerCommandDispatch::~ControllerCommandDispatch() +{ +} + +void ControllerCommandDispatch::initialize() +{ + if( !m_xChartController.is()) + return; + + rtl::Reference<::chart::ChartModel> xModel( m_xChartController->getChartModel()); + OSL_ASSERT( xModel.is()); + if( xModel.is()) + xModel->addModifyListener( this ); + + // Listen selection modifications (Arrangement feature - issue 63017). + if( m_xSelectionSupplier.is() ) + m_xSelectionSupplier->addSelectionChangeListener( this ); + + if( m_apModelState && xModel.is()) + m_apModelState->update( xModel ); + + if( m_apControllerState && xModel.is()) + m_apControllerState->update( m_xChartController, xModel ); + + updateCommandAvailability(); +} + +void ControllerCommandDispatch::fireStatusEventForURLImpl( + const OUString & rURL, + const Reference< frame::XStatusListener > & xSingleListener ) +{ + std::map< OUString, uno::Any >::const_iterator aArgIt( m_aCommandArguments.find( rURL )); + if( aArgIt != m_aCommandArguments.end()) + fireStatusEventForURL( rURL, aArgIt->second, commandAvailable( rURL ), xSingleListener ); + else + fireStatusEventForURL( rURL, uno::Any(), commandAvailable( rURL ), xSingleListener ); +} + +void ControllerCommandDispatch::updateCommandAvailability() +{ + bool bModelStateIsValid = (m_apModelState != nullptr); + bool bControllerStateIsValid = (m_apControllerState != nullptr); + // Model and controller states exist. + OSL_ASSERT( bModelStateIsValid ); + OSL_ASSERT( bControllerStateIsValid ); + + // read-only + bool bIsWritable = bModelStateIsValid && (! m_apModelState->bIsReadOnly); + bool bShapeContext = m_xChartController.is() && m_xChartController->isShapeContext(); + + bool bEnableDataTableDialog = false; + bool bCanCreateDataProvider = false; + + if ( m_xChartController.is() ) + { + Reference< beans::XPropertySet > xProps( m_xChartController->getModel(), uno::UNO_QUERY ); + if ( xProps.is() ) + { + try + { + xProps->getPropertyValue("EnableDataTableDialog") >>= bEnableDataTableDialog; + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("chart2", "" ); + } + } + + rtl::Reference< ChartModel > xChartModel = m_xChartController->getChartModel(); + OSL_ENSURE(xChartModel.is(), "Invalid XChartDocument"); + if ( xChartModel.is() ) + { + css::uno::Reference< com::sun::star::chart2::XDataProviderAccess > xCreatorDoc(xChartModel->getParent(), uno::UNO_QUERY); + bCanCreateDataProvider = xCreatorDoc.is(); + } + } + + // edit commands + m_aCommandAvailability[ ".uno:Cut" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bIsDeleteableObjectSelected; + m_aCommandAvailability[ ".uno:Copy" ] = bControllerStateIsValid && m_apControllerState->bHasSelectedObject; + m_aCommandAvailability[ ".uno:Paste" ] = bIsWritable; + + // toolbar commands + m_aCommandAvailability[ ".uno:ToggleGridHorizontal" ] = bIsWritable; + m_aCommandArguments[ ".uno:ToggleGridHorizontal" ] <<= bModelStateIsValid && m_apModelState->bHasMainYGrid; + m_aCommandAvailability[ ".uno:ToggleGridVertical" ] = bIsWritable; + m_aCommandArguments[ ".uno:ToggleGridVertical" ] <<= bModelStateIsValid && m_apModelState->bHasMainXGrid; + + m_aCommandAvailability[ ".uno:ToggleLegend" ] = bIsWritable; + m_aCommandArguments[ ".uno:ToggleLegend" ] <<= bModelStateIsValid && m_apModelState->bHasLegend; + + m_aCommandAvailability[ ".uno:NewArrangement" ] = bIsWritable; + m_aCommandAvailability[ ".uno:Update" ] = bIsWritable; + m_aCommandAvailability[ ".uno:DefaultColors" ] = bIsWritable; + m_aCommandAvailability[ ".uno:BarWidth" ] = bIsWritable; + m_aCommandAvailability[ ".uno:NumberOfLines" ] = bIsWritable; + m_aCommandAvailability[ ".uno:ArrangeRow" ] = + bShapeContext || ( bIsWritable && bControllerStateIsValid && ( m_apControllerState->bMayMoveSeriesForward || m_apControllerState->bMayMoveSeriesBackward ) ); + + // insert objects + m_aCommandAvailability[ ".uno:InsertTitles" ] = m_aCommandAvailability[ ".uno:InsertMenuTitles" ] = bIsWritable; + m_aCommandAvailability[ ".uno:InsertLegend" ] = m_aCommandAvailability[ ".uno:InsertMenuLegend" ] = bIsWritable; + m_aCommandAvailability[ ".uno:DeleteLegend" ] = bIsWritable; + m_aCommandAvailability[ ".uno:InsertMenuDataLabels" ] = bIsWritable; + m_aCommandAvailability[ ".uno:InsertRemoveAxes" ] = m_aCommandAvailability[ ".uno:InsertMenuAxes" ] = bIsWritable && m_apModelState->bSupportsAxes; + m_aCommandAvailability[ ".uno:InsertMenuGrids" ] = bIsWritable && m_apModelState->bSupportsAxes; + m_aCommandAvailability[ ".uno:InsertMenuTrendlines" ] = bIsWritable && m_apModelState->bSupportsStatistics && bControllerStateIsValid && m_apControllerState->bMayAddMenuTrendline; + m_aCommandAvailability[ ".uno:InsertMenuMeanValues" ] = bIsWritable && m_apModelState->bSupportsStatistics; + m_aCommandAvailability[ ".uno:InsertMenuXErrorBars" ] = bIsWritable && m_apModelState->bSupportsStatistics; + m_aCommandAvailability[ ".uno:InsertMenuYErrorBars" ] = bIsWritable && m_apModelState->bSupportsStatistics; + m_aCommandAvailability[ ".uno:InsertSymbol" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bIsTextObject; + m_aCommandAvailability[ ".uno:InsertMenuDataTable" ] = bIsWritable; + + // format objects + bool bFormatObjectAvailable = bIsWritable && bControllerStateIsValid && m_apControllerState->bIsFormateableObjectSelected; + m_aCommandAvailability[ ".uno:FormatSelection" ] = bFormatObjectAvailable; + m_aCommandAvailability[ ".uno:FormatAxis" ] = bFormatObjectAvailable; + m_aCommandAvailability[ ".uno:FormatTitle" ] = bFormatObjectAvailable; + m_aCommandAvailability[ ".uno:FormatDataSeries" ] = bFormatObjectAvailable; + m_aCommandAvailability[ ".uno:FormatDataPoint" ] = bFormatObjectAvailable; + m_aCommandAvailability[ ".uno:FormatDataLabels" ] = bFormatObjectAvailable; + m_aCommandAvailability[ ".uno:FormatDataLabel" ] = bFormatObjectAvailable; + m_aCommandAvailability[ ".uno:FormatXErrorBars" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayFormatXErrorBars; + m_aCommandAvailability[ ".uno:FormatYErrorBars" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayFormatYErrorBars; + m_aCommandAvailability[ ".uno:FormatMeanValue" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayFormatMeanValue; + m_aCommandAvailability[ ".uno:FormatTrendline" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayFormatTrendline; + m_aCommandAvailability[ ".uno:FormatTrendlineEquation" ] = bFormatObjectAvailable && bControllerStateIsValid && m_apControllerState->bMayFormatTrendlineEquation; + m_aCommandAvailability[ ".uno:FormatStockLoss" ] = bFormatObjectAvailable; + m_aCommandAvailability[ ".uno:FormatStockGain" ] = bFormatObjectAvailable; + + m_aCommandAvailability[ ".uno:DiagramType" ] = bIsWritable; + m_aCommandAvailability[ ".uno:Legend" ] = bIsWritable && m_apModelState->bHasLegend; + m_aCommandAvailability[ ".uno:DiagramWall" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasWall; + m_aCommandAvailability[ ".uno:DiagramArea" ] = bIsWritable; + + m_aCommandAvailability[ ".uno:TransformDialog" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bHasSelectedObject && m_apControllerState->bIsPositionableObject; + + // 3d commands + m_aCommandAvailability[ ".uno:View3D" ] = bIsWritable && bModelStateIsValid && m_apModelState->bIsThreeD; + m_aCommandAvailability[ ".uno:DiagramFloor" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasFloor; + + //some more format commands with different ui text + m_aCommandAvailability[ ".uno:FormatWall" ] = m_aCommandAvailability[ ".uno:DiagramWall" ]; + m_aCommandAvailability[ ".uno:FormatFloor" ] = m_aCommandAvailability[ ".uno:DiagramFloor" ]; + m_aCommandAvailability[ ".uno:FormatChartArea" ] = m_aCommandAvailability[ ".uno:DiagramArea" ]; + m_aCommandAvailability[ ".uno:FormatLegend" ] = m_aCommandAvailability[ ".uno:Legend" ]; + + // depending on own data and ability to create new data provider + m_aCommandAvailability[".uno:DataRanges"] = bIsWritable && bModelStateIsValid && !m_apModelState->bHasDataFromPivotTable + && ((m_apModelState->bHasOwnData && bCanCreateDataProvider) || !m_apModelState->bHasOwnData); + m_aCommandAvailability[ ".uno:DiagramData" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasOwnData && bEnableDataTableDialog; + + // titles + m_aCommandAvailability[ ".uno:MainTitle" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasMainTitle; + m_aCommandAvailability[ ".uno:SubTitle" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasSubTitle; + m_aCommandAvailability[ ".uno:XTitle" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasXAxisTitle; + m_aCommandAvailability[ ".uno:YTitle" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasYAxisTitle; + m_aCommandAvailability[ ".uno:ZTitle" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasZAxisTitle; + m_aCommandAvailability[ ".uno:SecondaryXTitle" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasSecondaryXAxisTitle; + m_aCommandAvailability[ ".uno:SecondaryYTitle" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasSecondaryYAxisTitle; + m_aCommandAvailability[ ".uno:AllTitles" ] = bIsWritable && bModelStateIsValid && m_apModelState->HasAnyTitle(); + + // text + m_aCommandAvailability[ ".uno:ScaleText" ] = bIsWritable && bModelStateIsValid ; + m_aCommandArguments[ ".uno:ScaleText" ] <<= bModelStateIsValid && m_apModelState->bHasAutoScaledText; + + // axes + m_aCommandAvailability[ ".uno:DiagramAxisX" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasXAxis; + m_aCommandAvailability[ ".uno:DiagramAxisY" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasYAxis; + m_aCommandAvailability[ ".uno:DiagramAxisZ" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasZAxis; + m_aCommandAvailability[ ".uno:DiagramAxisA" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasAAxis; + m_aCommandAvailability[ ".uno:DiagramAxisB" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasBAxis; + m_aCommandAvailability[ ".uno:DiagramAxisAll" ] = bIsWritable && bModelStateIsValid && m_apModelState->HasAnyAxis(); + + // grids + // note: x and y are swapped in the commands! + m_aCommandAvailability[ ".uno:DiagramGridYMain" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasMainXGrid; + m_aCommandAvailability[ ".uno:DiagramGridXMain" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasMainYGrid; + m_aCommandAvailability[ ".uno:DiagramGridZMain" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasMainZGrid; + m_aCommandAvailability[ ".uno:DiagramGridYHelp" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasHelpXGrid; + m_aCommandAvailability[ ".uno:DiagramGridXHelp" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasHelpYGrid; + m_aCommandAvailability[ ".uno:DiagramGridZHelp" ] = bIsWritable && bModelStateIsValid && m_apModelState->bHasHelpZGrid; + m_aCommandAvailability[ ".uno:DiagramGridAll" ] = bIsWritable && bModelStateIsValid && m_apModelState->HasAnyGrid(); + + // series arrangement + m_aCommandAvailability[ ".uno:Forward" ] = ( bShapeContext ? isShapeControllerCommandAvailable( ".uno:Forward" ) : + ( bIsWritable && bControllerStateIsValid && m_apControllerState->bMayMoveSeriesForward && bEnableDataTableDialog ) ); + m_aCommandAvailability[ ".uno:Backward" ] = ( bShapeContext ? isShapeControllerCommandAvailable( ".uno:Backward" ) : + ( bIsWritable && bControllerStateIsValid && m_apControllerState->bMayMoveSeriesBackward && bEnableDataTableDialog ) ); + + m_aCommandAvailability[ ".uno:InsertDataLabels" ] = bIsWritable; + m_aCommandAvailability[ ".uno:InsertDataLabel" ] = bIsWritable; + m_aCommandAvailability[ ".uno:InsertMeanValue" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayAddMeanValue; + m_aCommandAvailability[ ".uno:InsertTrendline" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayAddTrendline; + const bool bInsertTrendlineEquation = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayAddTrendlineEquation; + m_aCommandAvailability[ ".uno:InsertTrendlineEquation" ] = bInsertTrendlineEquation; + m_aCommandAvailability[ ".uno:InsertTrendlineEquationAndR2" ] = bInsertTrendlineEquation && m_apControllerState->bMayAddR2Value; + m_aCommandAvailability[ ".uno:InsertR2Value" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayAddR2Value + && !m_apControllerState->bMayAddTrendlineEquation; + m_aCommandAvailability[ ".uno:DeleteR2Value" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayDeleteR2Value; + + m_aCommandAvailability[ ".uno:InsertXErrorBars" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayAddXErrorBars; + m_aCommandAvailability[ ".uno:InsertYErrorBars" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayAddYErrorBars; + + m_aCommandAvailability[ ".uno:DeleteDataLabels" ] = bIsWritable; + m_aCommandAvailability[ ".uno:DeleteDataLabel" ] = bIsWritable; + m_aCommandAvailability[ ".uno:DeleteTrendline" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayDeleteTrendline; + m_aCommandAvailability[ ".uno:DeleteTrendlineEquation" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayDeleteTrendlineEquation; + m_aCommandAvailability[ ".uno:DeleteMeanValue" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayDeleteMeanValue; + m_aCommandAvailability[ ".uno:DeleteXErrorBars" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayDeleteXErrorBars; + m_aCommandAvailability[ ".uno:DeleteYErrorBars" ] = bIsWritable && bControllerStateIsValid && m_apControllerState->bMayDeleteYErrorBars; + + m_aCommandAvailability[ ".uno:ResetDataPoint" ] = bIsWritable; + m_aCommandAvailability[ ".uno:ResetAllDataPoints" ] = bIsWritable; + + m_aCommandAvailability[ ".uno:InsertAxis" ] = bIsWritable; + m_aCommandAvailability[ ".uno:DeleteAxis" ] = bIsWritable; + m_aCommandAvailability[ ".uno:InsertAxisTitle" ] = bIsWritable; + m_aCommandAvailability[ ".uno:FormatMajorGrid" ] = bIsWritable; + m_aCommandAvailability[ ".uno:InsertMajorGrid" ] = bIsWritable; + m_aCommandAvailability[ ".uno:DeleteMajorGrid" ] = bIsWritable; + m_aCommandAvailability[ ".uno:FormatMinorGrid" ] = bIsWritable; + m_aCommandAvailability[ ".uno:InsertMinorGrid" ] = bIsWritable; + m_aCommandAvailability[ ".uno:DeleteMinorGrid" ] = bIsWritable; + + // data table + m_aCommandAvailability[ ".uno:InsertDataTable" ] = bIsWritable && bModelStateIsValid && !m_apModelState->bDataTable; + m_aCommandAvailability[ ".uno:DeleteDataTable" ] = bIsWritable && bModelStateIsValid && m_apModelState->bDataTable; +} + +bool ControllerCommandDispatch::commandAvailable( const OUString & rCommand ) +{ + std::map< OUString, bool >::const_iterator aIt( m_aCommandAvailability.find( rCommand )); + if( aIt != m_aCommandAvailability.end()) + return aIt->second; + SAL_WARN("chart2", "commandAvailable: command not in availability map:" << rCommand); + return false; +} + +bool ControllerCommandDispatch::isShapeControllerCommandAvailable( const OUString& rCommand ) +{ + ShapeController* pShapeController(nullptr); + { + SolarMutexGuard g; + if (m_pDispatchContainer) + pShapeController = m_pDispatchContainer->getShapeController(); + } + if ( pShapeController ) + { + FeatureState aState( pShapeController->getState( rCommand ) ); + return aState.bEnabled; + } + return false; +} + +void ControllerCommandDispatch::fireStatusEvent( + const OUString & rURL, + const Reference< frame::XStatusListener > & xSingleListener /* = 0 */ ) +{ + bool bIsChartSelectorURL = rURL == ".uno:ChartElementSelector"; + if( rURL.isEmpty() || bIsChartSelectorURL ) + { + uno::Any aArg; + aArg <<= Reference< frame::XController >(m_xChartController); + fireStatusEventForURL( ".uno:ChartElementSelector", aArg, true, xSingleListener ); + } + + if( rURL.isEmpty() ) + { + for (auto const& elem : m_aCommandAvailability) + fireStatusEventForURLImpl( elem.first, xSingleListener ); + } + else if( !bIsChartSelectorURL ) + fireStatusEventForURLImpl( rURL, xSingleListener ); + + // statusbar. Should be handled by base implementation + // @todo: remove if Issue 68864 is fixed + if( rURL.isEmpty() || rURL == ".uno:StatusBarVisible" ) + { + bool bIsStatusBarVisible( lcl_isStatusBarVisible( m_xChartController )); + fireStatusEventForURL( ".uno:StatusBarVisible", uno::Any( bIsStatusBarVisible ), true, xSingleListener ); + } +} + +// ____ XDispatch ____ +void SAL_CALL ControllerCommandDispatch::dispatch( + const util::URL& URL, + const Sequence< beans::PropertyValue >& Arguments ) +{ + if( commandAvailable( URL.Complete )) + m_xDispatch->dispatch( URL, Arguments ); +} + +// ____ WeakComponentImplHelperBase ____ +/// is called when this is disposed +void ControllerCommandDispatch::disposing(std::unique_lock<std::mutex>& /*rGuard*/) +{ + m_xChartController.clear(); + m_xDispatch.clear(); + m_xSelectionSupplier.clear(); +} + +// ____ XEventListener (base of XModifyListener) ____ +void SAL_CALL ControllerCommandDispatch::disposing( const lang::EventObject& /* Source */ ) +{ + m_xChartController.clear(); + m_xDispatch.clear(); + m_xSelectionSupplier.clear(); +} + +// ____ XModifyListener ____ +void SAL_CALL ControllerCommandDispatch::modified( const lang::EventObject& aEvent ) +{ + bool bUpdateCommandAvailability = false; + + // Update the "ModelState" Struct. + if( m_apModelState && m_xChartController.is()) + { + m_apModelState->update( m_xChartController->getChartModel()); + bUpdateCommandAvailability = true; + } + + // Update the "ControllerState" Struct. + if( m_apControllerState && m_xChartController.is()) + { + m_apControllerState->update( m_xChartController, m_xChartController->getChartModel()); + bUpdateCommandAvailability = true; + } + + if( bUpdateCommandAvailability ) + updateCommandAvailability(); + + if (comphelper::LibreOfficeKit::isActive()) + { + if (SfxViewShell* pViewShell = SfxViewShell::Current()) + if (SfxObjectShell* pObjSh = pViewShell->GetObjectShell()) + pObjSh->SetModified(); + } + + CommandDispatch::modified( aEvent ); +} + +// ____ XSelectionChangeListener ____ +void SAL_CALL ControllerCommandDispatch::selectionChanged( const lang::EventObject& aEvent ) +{ + // Update the "ControllerState" Struct. + if( m_apControllerState && m_xChartController.is()) + { + m_apControllerState->update( m_xChartController, m_xChartController->getChartModel()); + updateCommandAvailability(); + } + + CommandDispatch::modified( aEvent ); +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ControllerCommandDispatch.hxx b/chart2/source/controller/main/ControllerCommandDispatch.hxx new file mode 100644 index 0000000000..6a5e441e8e --- /dev/null +++ b/chart2/source/controller/main/ControllerCommandDispatch.hxx @@ -0,0 +1,119 @@ +/* -*- 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 . + */ +#pragma once + +#include "CommandDispatch.hxx" +#include <com/sun/star/view/XSelectionChangeListener.hpp> +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> + +#include <memory> + +namespace com::sun::star::view { class XSelectionSupplier; } + +namespace chart +{ + +class ChartController; +class CommandDispatchContainer; + +namespace impl +{ +struct ModelState; +struct ControllerState; + +// #i63017# : need to implement the XSelectionChangeListener in order +// to update the ControllerState when the selection changes. +typedef ::cppu::ImplInheritanceHelper< + CommandDispatch, + css::view::XSelectionChangeListener > + ControllerCommandDispatch_Base; +} + +/** This class is a CommandDispatch that is responsible for all commands that + the ChartController supports. + + This class determines which commands are currently available (via the model + state) and if an available command is called forwards it to the + ChartController. + */ +class ControllerCommandDispatch : public impl::ControllerCommandDispatch_Base +{ +public: + explicit ControllerCommandDispatch( + const css::uno::Reference< css::uno::XComponentContext > & xContext, + ChartController* pController, CommandDispatchContainer* pContainer ); + virtual ~ControllerCommandDispatch() override; + + // late initialisation, especially for adding as listener + virtual void initialize() override; + + bool commandAvailable( const OUString & rCommand ); + +protected: + // ____ XDispatch ____ + virtual void SAL_CALL dispatch( + const css::util::URL& URL, + const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + + // ____ WeakComponentImplHelperBase ____ + /// is called when this is disposed + virtual void disposing(std::unique_lock<std::mutex>& rGuard) override; + + // ____ XEventListener (base of XModifyListener) ____ + virtual void SAL_CALL disposing( + const css::lang::EventObject& Source ) override; + + virtual void fireStatusEvent( + const OUString & rURL, + const css::uno::Reference< css::frame::XStatusListener > & xSingleListener ) override; + + // ____ XModifyListener ____ + virtual void SAL_CALL modified( + const css::lang::EventObject& aEvent ) override; + + // ____ XSelectionChangeListener ____ + virtual void SAL_CALL selectionChanged( + const css::lang::EventObject& aEvent ) override; + +private: + void fireStatusEventForURLImpl( + const OUString & rURL, + const css::uno::Reference< css::frame::XStatusListener > & xSingleListener ); + + void updateCommandAvailability(); + + bool isShapeControllerCommandAvailable( const OUString& rCommand ); + + rtl::Reference<ChartController> m_xChartController; + css::uno::Reference< css::view::XSelectionSupplier > m_xSelectionSupplier; + css::uno::Reference< css::frame::XDispatch > m_xDispatch; + + std::unique_ptr< impl::ModelState > m_apModelState; + std::unique_ptr< impl::ControllerState > m_apControllerState; + + mutable std::map< OUString, bool > m_aCommandAvailability; + mutable std::map< OUString, css::uno::Any > m_aCommandArguments; + + CommandDispatchContainer* m_pDispatchContainer; +}; + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/DragMethod_Base.cxx b/chart2/source/controller/main/DragMethod_Base.cxx new file mode 100644 index 0000000000..5e46dc327b --- /dev/null +++ b/chart2/source/controller/main/DragMethod_Base.cxx @@ -0,0 +1,75 @@ +/* -*- 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 "DragMethod_Base.hxx" +#include <DrawViewWrapper.hxx> +#include <ChartModel.hxx> +#include <ObjectNameProvider.hxx> +#include <ObjectIdentifier.hxx> + +#include <svx/ActionDescriptionProvider.hxx> +#include <utility> +#include <vcl/ptrstyle.hxx> + +namespace chart +{ + +using namespace ::com::sun::star; + +DragMethod_Base::DragMethod_Base( DrawViewWrapper& rDrawViewWrapper + , OUString aObjectCID + , const rtl::Reference<::chart::ChartModel>& xChartModel + , ActionDescriptionProvider::ActionType eActionType ) + : SdrDragMethod( rDrawViewWrapper ) + , m_rDrawViewWrapper(rDrawViewWrapper) + , m_aObjectCID(std::move(aObjectCID)) + , m_eActionType( eActionType ) + , m_xChartModel( xChartModel.get() ) +{ + setMoveOnly(true); +} +DragMethod_Base::~DragMethod_Base() +{ +} + +rtl::Reference<::chart::ChartModel> DragMethod_Base::getChartModel() const +{ + return m_xChartModel.get(); +} + +OUString DragMethod_Base::getUndoDescription() const +{ + return ActionDescriptionProvider::createDescription( + m_eActionType, + ObjectNameProvider::getName( ObjectIdentifier::getObjectType( m_aObjectCID ))); +} +OUString DragMethod_Base::GetSdrDragComment() const +{ + return getUndoDescription(); +} +PointerStyle DragMethod_Base::GetSdrDragPointer() const +{ + if( IsDraggingPoints() || IsDraggingGluePoints() ) + return PointerStyle::MovePoint; + else + return PointerStyle::Move; +} +} //namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/DragMethod_Base.hxx b/chart2/source/controller/main/DragMethod_Base.hxx new file mode 100644 index 0000000000..1a65938890 --- /dev/null +++ b/chart2/source/controller/main/DragMethod_Base.hxx @@ -0,0 +1,59 @@ +/* -*- 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 . + */ +#pragma once + +#include <svx/ActionDescriptionProvider.hxx> +#include <svx/svddrgmt.hxx> +#include <unotools/weakref.hxx> + +namespace chart { class DrawViewWrapper; } +namespace com::sun::star::frame { class XModel; } + +namespace chart +{ +class ChartModel; + +class DragMethod_Base : public SdrDragMethod +{ +public: + DragMethod_Base( DrawViewWrapper& rDrawViewWrapper, OUString aObjectCID + , const rtl::Reference<::chart::ChartModel>& xChartModel + , ActionDescriptionProvider::ActionType eActionType = ActionDescriptionProvider::ActionType::Move ); + virtual ~DragMethod_Base() override; + + OUString getUndoDescription() const; + + virtual OUString GetSdrDragComment() const override; + virtual PointerStyle GetSdrDragPointer() const override; + +protected: + rtl::Reference<::chart::ChartModel> getChartModel() const; + +protected: + DrawViewWrapper& m_rDrawViewWrapper; + OUString m_aObjectCID; + ActionDescriptionProvider::ActionType m_eActionType; + +private: + unotools::WeakReference<::chart::ChartModel> m_xChartModel; +}; + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/DragMethod_PieSegment.cxx b/chart2/source/controller/main/DragMethod_PieSegment.cxx new file mode 100644 index 0000000000..5bd85faaf9 --- /dev/null +++ b/chart2/source/controller/main/DragMethod_PieSegment.cxx @@ -0,0 +1,151 @@ +/* -*- 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 "DragMethod_PieSegment.hxx" +#include <DrawViewWrapper.hxx> +#include <ChartModel.hxx> +#include <strings.hrc> +#include <ResId.hxx> +#include <ObjectIdentifier.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/Point.hpp> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <comphelper/diagnose_ex.hxx> + +namespace chart +{ + +using namespace ::com::sun::star; +using ::com::sun::star::uno::Reference; +using ::basegfx::B2DVector; + +DragMethod_PieSegment::DragMethod_PieSegment( DrawViewWrapper& rDrawViewWrapper + , const OUString& rObjectCID + , const rtl::Reference<::chart::ChartModel>& xChartModel ) + : DragMethod_Base( rDrawViewWrapper, rObjectCID, xChartModel ) + , m_aStartVector(100.0,100.0) + , m_fInitialOffset(0.0) + , m_fAdditionalOffset(0.0) + , m_aDragDirection(1000.0,1000.0) + , m_fDragRange( 1.0 ) +{ + std::u16string_view aParameter( ObjectIdentifier::getDragParameterString( m_aObjectCID ) ); + + sal_Int32 nOffsetPercent(0); + awt::Point aMinimumPosition(0,0); + awt::Point aMaximumPosition(0,0); + + ObjectIdentifier::parsePieSegmentDragParameterString( + aParameter, nOffsetPercent, aMinimumPosition, aMaximumPosition ); + + m_fInitialOffset = nOffsetPercent / 100.0; + if( m_fInitialOffset < 0.0 ) + m_fInitialOffset = 0.0; + if( m_fInitialOffset > 1.0 ) + m_fInitialOffset = 1.0; + B2DVector aMinVector( aMinimumPosition.X, aMinimumPosition.Y ); + B2DVector aMaxVector( aMaximumPosition.X, aMaximumPosition.Y ); + m_aDragDirection = aMaxVector - aMinVector; + m_fDragRange = m_aDragDirection.scalar( m_aDragDirection ); + if( m_fDragRange == 0.0 ) + m_fDragRange = 1.0; +} +DragMethod_PieSegment::~DragMethod_PieSegment() +{ +} +OUString DragMethod_PieSegment::GetSdrDragComment() const +{ + OUString aStr = SchResId(STR_STATUS_PIE_SEGMENT_EXPLODED); + aStr = aStr.replaceFirst( "%PERCENTVALUE", OUString::number( static_cast<sal_Int32>((m_fAdditionalOffset+m_fInitialOffset)*100.0) )); + return aStr; +} +bool DragMethod_PieSegment::BeginSdrDrag() +{ + Point aStart( DragStat().GetStart() ); + m_aStartVector = B2DVector( aStart.X(), aStart.Y() ); + Show(); + return true; +} +void DragMethod_PieSegment::MoveSdrDrag(const Point& rPnt) +{ + if( !DragStat().CheckMinMoved(rPnt) ) + return; + + //calculate new offset + B2DVector aShiftVector( B2DVector( rPnt.X(), rPnt.Y() ) - m_aStartVector ); + m_fAdditionalOffset = m_aDragDirection.scalar( aShiftVector )/m_fDragRange; // projection + + if( m_fAdditionalOffset < -m_fInitialOffset ) + m_fAdditionalOffset = -m_fInitialOffset; + else if( m_fAdditionalOffset > (1.0-m_fInitialOffset) ) + m_fAdditionalOffset = 1.0 - m_fInitialOffset; + + B2DVector aNewPosVector = m_aStartVector + (m_aDragDirection * m_fAdditionalOffset); + Point aNewPos( static_cast<tools::Long>(aNewPosVector.getX()), static_cast<tools::Long>(aNewPosVector.getY()) ); + if( aNewPos != DragStat().GetNow() ) + { + Hide(); + DragStat().NextMove( aNewPos ); + Show(); + } +} +bool DragMethod_PieSegment::EndSdrDrag(bool /*bCopy*/) +{ + Hide(); + + try + { + rtl::Reference<::chart::ChartModel> xChartModel( getChartModel() ); + if( xChartModel.is() ) + { + Reference< beans::XPropertySet > xPointProperties( + ObjectIdentifier::getObjectPropertySet( m_aObjectCID, xChartModel ) ); + if( xPointProperties.is() ) + xPointProperties->setPropertyValue( "Offset", uno::Any( m_fAdditionalOffset+m_fInitialOffset )); + } + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + + return true; +} +basegfx::B2DHomMatrix DragMethod_PieSegment::getCurrentTransformation() const +{ + basegfx::B2DHomMatrix aRetval; + + aRetval.translate(DragStat().GetDX(), DragStat().GetDY()); + + return aRetval; +} +void DragMethod_PieSegment::createSdrDragEntries() +{ + SdrObject* pObj = m_rDrawViewWrapper.getSelectedObject(); + SdrPageView* pPV = m_rDrawViewWrapper.GetPageView(); + + if( pObj && pPV ) + { + basegfx::B2DPolyPolygon aNewPolyPolygon(pObj->TakeXorPoly()); + addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(std::move(aNewPolyPolygon)))); + } +} +} //namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/DragMethod_PieSegment.hxx b/chart2/source/controller/main/DragMethod_PieSegment.hxx new file mode 100644 index 0000000000..8cb498373f --- /dev/null +++ b/chart2/source/controller/main/DragMethod_PieSegment.hxx @@ -0,0 +1,54 @@ +/* -*- 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 . + */ +#pragma once + +#include "DragMethod_Base.hxx" +#include <basegfx/vector/b2dvector.hxx> + +namespace chart +{ + +class DragMethod_PieSegment : public DragMethod_Base +{ +public: + DragMethod_PieSegment( DrawViewWrapper& rDrawViewWrapper, const OUString& rObjectCID + , const rtl::Reference<::chart::ChartModel>& xChartModel ); + virtual ~DragMethod_PieSegment() override; + + virtual OUString GetSdrDragComment() const override; + virtual bool BeginSdrDrag() override; + virtual void MoveSdrDrag(const Point& rPnt) override; + virtual bool EndSdrDrag(bool bCopy) override; + + virtual basegfx::B2DHomMatrix getCurrentTransformation() const override; + +protected: + virtual void createSdrDragEntries() override; + +private: + ::basegfx::B2DVector m_aStartVector; + double m_fInitialOffset; + double m_fAdditionalOffset; + ::basegfx::B2DVector m_aDragDirection; + double m_fDragRange; +}; + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/DragMethod_RotateDiagram.cxx b/chart2/source/controller/main/DragMethod_RotateDiagram.cxx new file mode 100644 index 0000000000..0ae2f2803c --- /dev/null +++ b/chart2/source/controller/main/DragMethod_RotateDiagram.cxx @@ -0,0 +1,230 @@ +/* -*- 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 "DragMethod_RotateDiagram.hxx" +#include <DrawViewWrapper.hxx> + +#include <SelectionHelper.hxx> +#include <ChartModelHelper.hxx> +#include <ChartModel.hxx> +#include <DiagramHelper.hxx> +#include <Diagram.hxx> +#include <ChartType.hxx> +#include <ChartTypeHelper.hxx> +#include <ThreeDHelper.hxx> +#include <defines.hxx> +#include <svx/sdr/overlay/overlaypolypolygon.hxx> + +#include <svx/scene3d.hxx> +#include <basegfx/matrix/b3dhommatrix.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <svx/sdr/contact/viewcontactofe3dscene.hxx> +#include <drawinglayer/geometry/viewinformation3d.hxx> + +namespace chart +{ + +using namespace ::com::sun::star; + +DragMethod_RotateDiagram::DragMethod_RotateDiagram( DrawViewWrapper& rDrawViewWrapper + , const OUString& rObjectCID + , const rtl::Reference<::chart::ChartModel>& xChartModel + , RotationDirection eRotationDirection ) + : DragMethod_Base( rDrawViewWrapper, rObjectCID, xChartModel, ActionDescriptionProvider::ActionType::Rotate ) + , m_pScene(nullptr) + , m_aReferenceRect(100,100,100,100) + , m_aStartPos(0,0) + , m_fInitialXAngleRad(0.0) + , m_fInitialYAngleRad(0.0) + , m_fInitialZAngleRad(0.0) + , m_fAdditionalXAngleRad(0.0) + , m_fAdditionalYAngleRad(0.0) + , m_fAdditionalZAngleRad(0.0) + , m_nInitialHorizontalAngleDegree(0) + , m_nInitialVerticalAngleDegree(0) + , m_nAdditionalHorizontalAngleDegree(0) + , m_nAdditionalVerticalAngleDegree(0) + , m_eRotationDirection(eRotationDirection) + , m_bRightAngledAxes(false) +{ + m_pScene = SelectionHelper::getSceneToRotate( rDrawViewWrapper.getNamedSdrObject( rObjectCID ) ); + SdrObject* pObj = rDrawViewWrapper.getSelectedObject(); + if(!(pObj && m_pScene)) + return; + + m_aReferenceRect = pObj->GetLogicRect(); + + m_aWireframePolyPolygon = m_pScene->CreateWireframe(); + + rtl::Reference< Diagram > xDiagram = getChartModel()->getFirstChartDiagram(); + if( !xDiagram.is() ) + return; + + xDiagram->getRotation( + m_nInitialHorizontalAngleDegree, m_nInitialVerticalAngleDegree ); + + xDiagram->getRotationAngle( + m_fInitialXAngleRad, m_fInitialYAngleRad, m_fInitialZAngleRad ); + + if( ChartTypeHelper::isSupportingRightAngledAxes( + xDiagram->getChartTypeByIndex( 0 ) ) ) + xDiagram->getPropertyValue("RightAngledAxes") >>= m_bRightAngledAxes; + if(m_bRightAngledAxes) + { + if( m_eRotationDirection==ROTATIONDIRECTION_Z ) + m_eRotationDirection=ROTATIONDIRECTION_FREE; + ThreeDHelper::adaptRadAnglesForRightAngledAxes( m_fInitialXAngleRad, m_fInitialYAngleRad ); + } +} +DragMethod_RotateDiagram::~DragMethod_RotateDiagram() +{ +} +OUString DragMethod_RotateDiagram::GetSdrDragComment() const +{ + return OUString(); +} +bool DragMethod_RotateDiagram::BeginSdrDrag() +{ + m_aStartPos = DragStat().GetStart(); + Show(); + return true; +} +void DragMethod_RotateDiagram::MoveSdrDrag(const Point& rPnt) +{ + if( !DragStat().CheckMinMoved(rPnt) ) + return; + + Hide(); + + //calculate new angle + double fX = M_PI_2 * static_cast<double>(rPnt.Y() - m_aStartPos.Y()) + / (m_aReferenceRect.GetHeight() > 0 ? static_cast<double>(m_aReferenceRect.GetHeight()) : 1.0); + double fY = M_PI * static_cast<double>(rPnt.X() - m_aStartPos.X()) + / (m_aReferenceRect.GetWidth() > 0 ? static_cast<double>(m_aReferenceRect.GetWidth()) : 1.0); + + if( m_eRotationDirection != ROTATIONDIRECTION_Y ) + m_fAdditionalYAngleRad = fY; + else + m_fAdditionalYAngleRad = 0.0; + if( m_eRotationDirection != ROTATIONDIRECTION_X ) + m_fAdditionalXAngleRad = fX; + else + m_fAdditionalXAngleRad = 0.0; + m_fAdditionalZAngleRad = 0.0; + + if( m_eRotationDirection == ROTATIONDIRECTION_Z ) + { + m_fAdditionalXAngleRad = 0.0; + m_fAdditionalYAngleRad = 0.0; + + double fCx = m_aReferenceRect.Center().X(); + double fCy = m_aReferenceRect.Center().Y(); + + m_fAdditionalZAngleRad = atan((fCx - m_aStartPos.X())/(m_aStartPos.Y()-fCy)) + + atan((fCx - rPnt.X())/(fCy-rPnt.Y())); + } + + m_nAdditionalHorizontalAngleDegree = static_cast<sal_Int32>(basegfx::rad2deg(m_fAdditionalXAngleRad)); + m_nAdditionalVerticalAngleDegree = -static_cast<sal_Int32>(basegfx::rad2deg(m_fAdditionalYAngleRad)); + + DragStat().NextMove(rPnt); + Show(); +} +bool DragMethod_RotateDiagram::EndSdrDrag(bool /*bCopy*/) +{ + Hide(); + + if( m_bRightAngledAxes || m_eRotationDirection==ROTATIONDIRECTION_Z ) + { + double fResultX = m_fInitialXAngleRad + m_fAdditionalXAngleRad; + double fResultY = m_fInitialYAngleRad + m_fAdditionalYAngleRad; + double fResultZ = m_fInitialZAngleRad + m_fAdditionalZAngleRad; + + if(m_bRightAngledAxes) + ThreeDHelper::adaptRadAnglesForRightAngledAxes( fResultX, fResultY ); + + rtl::Reference<Diagram> xDiagram = getChartModel()->getFirstChartDiagram(); + if (xDiagram) + xDiagram->setRotationAngle( fResultX, fResultY, fResultZ ); + } + else + { + rtl::Reference<Diagram> xDiagram = getChartModel()->getFirstChartDiagram(); + if (xDiagram) + xDiagram->setRotation( + m_nInitialHorizontalAngleDegree+m_nAdditionalHorizontalAngleDegree, m_nInitialVerticalAngleDegree+m_nAdditionalVerticalAngleDegree ); + } + + return true; +} +void DragMethod_RotateDiagram::CreateOverlayGeometry( + sdr::overlay::OverlayManager& rOverlayManager, + const sdr::contact::ObjectContact& rObjectContact) +{ + ::basegfx::B3DHomMatrix aCurrentTransform; + aCurrentTransform.translate( -FIXED_SIZE_FOR_3D_CHART_VOLUME/2.0, + -FIXED_SIZE_FOR_3D_CHART_VOLUME/2.0, + -FIXED_SIZE_FOR_3D_CHART_VOLUME/2.0 ); + + double fResultX = m_fInitialXAngleRad + m_fAdditionalXAngleRad; + double fResultY = m_fInitialYAngleRad + m_fAdditionalYAngleRad; + double fResultZ = m_fInitialZAngleRad + m_fAdditionalZAngleRad; + + if(!m_bRightAngledAxes) + { + if( m_eRotationDirection!=ROTATIONDIRECTION_Z ) + { + ThreeDHelper::convertElevationRotationDegToXYZAngleRad( + m_nInitialHorizontalAngleDegree+m_nAdditionalHorizontalAngleDegree, -(m_nInitialVerticalAngleDegree+m_nAdditionalVerticalAngleDegree) + , fResultX, fResultY, fResultZ ); + } + aCurrentTransform.rotate( fResultX, fResultY, fResultZ ); + } + else + { + ThreeDHelper::adaptRadAnglesForRightAngledAxes( fResultX, fResultY ); + aCurrentTransform.shearXY(fResultY,-fResultX); + } + + if(!(m_aWireframePolyPolygon.count() && m_pScene)) + return; + + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(m_pScene->GetViewContact()); + const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D()); + const basegfx::B3DHomMatrix aWorldToView(aViewInfo3D.getDeviceToView() * aViewInfo3D.getProjection() * aViewInfo3D.getOrientation()); + const basegfx::B3DHomMatrix aTransform(aWorldToView * aCurrentTransform); + + // transform to relative scene coordinates + basegfx::B2DPolyPolygon aPolyPolygon(basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(m_aWireframePolyPolygon, aTransform)); + + // transform to 2D view coordinates + aPolyPolygon.transform(rVCScene.getObjectTransformation()); + + std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew( + new sdr::overlay::OverlayPolyPolygonStripedAndFilled( + std::move(aPolyPolygon))); + + insertNewlyCreatedOverlayObjectForSdrDragMethod( + std::move(pNew), + rObjectContact, + rOverlayManager); +} +} //namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/DragMethod_RotateDiagram.hxx b/chart2/source/controller/main/DragMethod_RotateDiagram.hxx new file mode 100644 index 0000000000..69e9050eeb --- /dev/null +++ b/chart2/source/controller/main/DragMethod_RotateDiagram.hxx @@ -0,0 +1,86 @@ +/* -*- 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 . + */ +#pragma once + +#include "DragMethod_Base.hxx" + +#include <basegfx/polygon/b3dpolypolygon.hxx> + +class E3dScene; +namespace chart { class DrawViewWrapper; } + +namespace chart +{ + +class DragMethod_RotateDiagram : public DragMethod_Base +{ +public: + enum RotationDirection + { + ROTATIONDIRECTION_FREE, + ROTATIONDIRECTION_X, + ROTATIONDIRECTION_Y, + ROTATIONDIRECTION_Z + }; + + DragMethod_RotateDiagram( DrawViewWrapper& rDrawViewWrapper + , const OUString& rObjectCID + , const rtl::Reference<::chart::ChartModel>& xChartModel + , RotationDirection eRotationDirection + ); + virtual ~DragMethod_RotateDiagram() override; + + virtual OUString GetSdrDragComment() const override; + + virtual bool BeginSdrDrag() override; + virtual void MoveSdrDrag(const Point& rPnt) override; + virtual bool EndSdrDrag(bool bCopy) override; + + virtual void CreateOverlayGeometry( + sdr::overlay::OverlayManager& rOverlayManager, + const sdr::contact::ObjectContact& rObjectContact) override; + +private: + E3dScene* m_pScene; + + tools::Rectangle m_aReferenceRect; + Point m_aStartPos; + basegfx::B3DPolyPolygon m_aWireframePolyPolygon; + + double m_fInitialXAngleRad; + double m_fInitialYAngleRad; + double m_fInitialZAngleRad; + + double m_fAdditionalXAngleRad; + double m_fAdditionalYAngleRad; + double m_fAdditionalZAngleRad; + + sal_Int32 m_nInitialHorizontalAngleDegree; + sal_Int32 m_nInitialVerticalAngleDegree; + + sal_Int32 m_nAdditionalHorizontalAngleDegree; + sal_Int32 m_nAdditionalVerticalAngleDegree; + + RotationDirection m_eRotationDirection; + bool m_bRightAngledAxes; +}; + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/DrawCommandDispatch.cxx b/chart2/source/controller/main/DrawCommandDispatch.cxx new file mode 100644 index 0000000000..1446059a02 --- /dev/null +++ b/chart2/source/controller/main/DrawCommandDispatch.cxx @@ -0,0 +1,613 @@ +/* -*- 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 "DrawCommandDispatch.hxx" +#include <ChartController.hxx> +#include <DrawViewWrapper.hxx> +#include <chartview/DrawModelWrapper.hxx> + +#include <com/sun/star/frame/CommandGroup.hpp> +#include <o3tl/unsafe_downcast.hxx> +#include <o3tl/string_view.hxx> +#include <vcl/svapp.hxx> +#include <svl/itempool.hxx> +#include <editeng/eeitem.hxx> +#include <svx/strings.hrc> +#include <svx/dialmgr.hxx> +#include <svx/fmmodel.hxx> +#include <svx/gallery.hxx> +#include <svx/svdoashp.hxx> +#include <svx/svdocapt.hxx> +#include <svx/svdopath.hxx> +#include <svx/svdpage.hxx> +#include <svx/unoapi.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xlnedwit.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/xtable.hxx> +#include <svx/sdtagitm.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::frame; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + + +namespace chart +{ + +DrawCommandDispatch::DrawCommandDispatch( const Reference< uno::XComponentContext >& rxContext, + ChartController* pController ) + :FeatureCommandDispatchBase( rxContext ) + ,m_pChartController( pController ) +{ +} + +DrawCommandDispatch::~DrawCommandDispatch() +{ +} + +bool DrawCommandDispatch::isFeatureSupported( const OUString& rCommandURL ) +{ + ChartCommandID nFeatureId = ChartCommandID::NONE; + OUString aBaseCommand; + OUString aCustomShapeType; + return parseCommandURL( rCommandURL, &nFeatureId, &aBaseCommand, &aCustomShapeType ); +} + +static ::basegfx::B2DPolyPolygon getPolygon(TranslateId pResId, const SdrModel& rModel) +{ + ::basegfx::B2DPolyPolygon aReturn; + XLineEndListRef pLineEndList = rModel.GetLineEndList(); + if ( pLineEndList.is() ) + { + OUString aName(SvxResId(pResId)); + tools::Long nCount = pLineEndList->Count(); + for ( tools::Long nIndex = 0; nIndex < nCount; ++nIndex ) + { + const XLineEndEntry* pEntry = pLineEndList->GetLineEnd(nIndex); + if ( pEntry->GetName() == aName ) + { + aReturn = pEntry->GetLineEnd(); + break; + } + } + } + return aReturn; +} + +void DrawCommandDispatch::setAttributes( SdrObject* pObj ) +{ + if ( !m_pChartController ) + return; + + DrawModelWrapper* pDrawModelWrapper = m_pChartController->GetDrawModelWrapper(); + DrawViewWrapper* pDrawViewWrapper = m_pChartController->GetDrawViewWrapper(); + if ( !(pDrawModelWrapper && pDrawViewWrapper && pDrawViewWrapper->GetCurrentObjIdentifier() == SdrObjKind::CustomShape) ) + return; + + bool bAttributesAppliedFromGallery = false; + if ( GalleryExplorer::GetSdrObjCount( GALLERY_THEME_POWERPOINT ) ) + { + std::vector< OUString > aObjList; + if ( GalleryExplorer::FillObjListTitle( GALLERY_THEME_POWERPOINT, aObjList ) ) + { + for ( size_t i = 0; i < aObjList.size(); ++i ) + { + if ( aObjList[ i ].equalsIgnoreAsciiCase( m_aCustomShapeType ) ) + { + FmFormModel aModel; + SfxItemPool& rPool(aModel.GetItemPool()); + rPool.FreezeIdRanges(); + + if ( GalleryExplorer::GetSdrObj( GALLERY_THEME_POWERPOINT, i, &aModel ) ) + { + const SdrObject* pSourceObj = aModel.GetPage( 0 )->GetObj( 0 ); + if ( pSourceObj ) + { + const SfxItemSet& rSource = pSourceObj->GetMergedItemSet(); + SfxItemSetFixed< + // Ranges from SdrAttrObj: + SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_MISC_FIRST, + SDRATTR_MISC_LAST, + SDRATTR_TEXTDIRECTION, + SDRATTR_TEXTDIRECTION, + // Graphic attributes, 3D + // properties, CustomShape + // properties: + SDRATTR_GRAF_FIRST, + SDRATTR_CUSTOMSHAPE_LAST, + // Range from SdrTextObj: + EE_ITEMS_START, EE_ITEMS_END> + aDest(pObj->getSdrModelFromSdrObject().GetItemPool()); + aDest.Set( rSource ); + pObj->SetMergedItemSet( aDest ); + Degree100 nAngle = pSourceObj->GetRotateAngle(); + if ( nAngle ) + pObj->NbcRotate( pObj->GetSnapRect().Center(), nAngle ); + bAttributesAppliedFromGallery = true; + } + } + break; + } + } + } + } + if ( !bAttributesAppliedFromGallery ) + { + pObj->SetMergedItem( SdrTextVertAdjustItem( SDRTEXTVERTADJUST_CENTER ) ); + pObj->SetMergedItem( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_BLOCK ) ); + pObj->SetMergedItem( makeSdrTextAutoGrowHeightItem( false ) ); + + o3tl::unsafe_downcast< SdrObjCustomShape* >( pObj )->MergeDefaultAttributes( &m_aCustomShapeType ); + } +} + +void DrawCommandDispatch::setLineEnds( SfxItemSet& rAttr ) +{ + if ( !(m_nFeatureId == ChartCommandID::DrawLineArrowEnd && m_pChartController) ) + return; + + DrawModelWrapper* pDrawModelWrapper = m_pChartController->GetDrawModelWrapper(); + DrawViewWrapper* pDrawViewWrapper = m_pChartController->GetDrawViewWrapper(); + if ( !(pDrawModelWrapper && pDrawViewWrapper) ) + return; + + ::basegfx::B2DPolyPolygon aArrow( getPolygon( RID_SVXSTR_ARROW, pDrawModelWrapper->getSdrModel() ) ); + if ( !aArrow.count() ) + { + ::basegfx::B2DPolygon aNewArrow; + aNewArrow.append( ::basegfx::B2DPoint( 10.0, 0.0 ) ); + aNewArrow.append( ::basegfx::B2DPoint( 0.0, 30.0) ); + aNewArrow.append( ::basegfx::B2DPoint( 20.0, 30.0 ) ); + aNewArrow.setClosed( true ); + aArrow.append( aNewArrow ); + } + + SfxItemSet aSet(pDrawViewWrapper->GetModel().GetItemPool()); + pDrawViewWrapper->GetAttributes( aSet ); + + tools::Long nWidth = 300; // (1/100th mm) + if ( aSet.GetItemState( XATTR_LINEWIDTH ) != SfxItemState::DONTCARE ) + { + tools::Long nValue = aSet.Get( XATTR_LINEWIDTH ).GetValue(); + if ( nValue > 0 ) + { + nWidth = nValue * 3; + } + } + + rAttr.Put( XLineEndItem( SvxResId( RID_SVXSTR_ARROW ), aArrow ) ); + rAttr.Put( XLineEndWidthItem( nWidth ) ); +} + +// WeakComponentImplHelperBase +void DrawCommandDispatch::disposing(std::unique_lock<std::mutex>& /*rGuard*/) +{ +} + +// XEventListener +void DrawCommandDispatch::disposing( const lang::EventObject& /* Source */ ) +{ +} + +FeatureState DrawCommandDispatch::getState( const OUString& rCommand ) +{ + FeatureState aReturn; + aReturn.bEnabled = false; + aReturn.aState <<= false; + + ChartCommandID nFeatureId = ChartCommandID::NONE; + OUString aBaseCommand; + OUString aCustomShapeType; + if ( parseCommandURL( rCommand, &nFeatureId, &aBaseCommand, &aCustomShapeType ) ) + { + switch ( nFeatureId ) + { + case ChartCommandID::DrawObjectSelect: + case ChartCommandID::DrawLine: + case ChartCommandID::DrawLineArrowEnd: + case ChartCommandID::DrawRect: + case ChartCommandID::DrawEllipse: + case ChartCommandID::DrawFreelineNoFill: + case ChartCommandID::DrawText: + case ChartCommandID::DrawCaption: + case ChartCommandID::DrawToolboxCsBasic: + case ChartCommandID::DrawToolboxCsSymbol: + case ChartCommandID::DrawToolboxCsArrow: + case ChartCommandID::DrawToolboxCsFlowchart: + case ChartCommandID::DrawToolboxCsCallout: + case ChartCommandID::DrawToolboxCsStar: + { + aReturn.bEnabled = true; + aReturn.aState <<= false; + } + break; + default: + { + aReturn.bEnabled = false; + aReturn.aState <<= false; + } + break; + } + } + + return aReturn; +} + +void DrawCommandDispatch::execute( const OUString& rCommand, const Sequence< beans::PropertyValue>& rArgs ) +{ + ChartDrawMode eDrawMode = CHARTDRAW_SELECT; + SdrObjKind eKind = SdrObjKind::NONE; + + ChartCommandID nFeatureId = ChartCommandID::NONE; + OUString aBaseCommand; + OUString aCustomShapeType; + if ( !parseCommandURL( rCommand, &nFeatureId, &aBaseCommand, &aCustomShapeType ) ) + return; + + bool bCreate = false; + m_nFeatureId = nFeatureId; + m_aCustomShapeType = aCustomShapeType; + + switch ( nFeatureId ) + { + case ChartCommandID::DrawObjectSelect: + { + eDrawMode = CHARTDRAW_SELECT; + eKind = SdrObjKind::NONE; + } + break; + case ChartCommandID::DrawLine: + case ChartCommandID::DrawLineArrowEnd: + { + eDrawMode = CHARTDRAW_INSERT; + eKind = SdrObjKind::Line; + } + break; + case ChartCommandID::DrawRect: + { + eDrawMode = CHARTDRAW_INSERT; + eKind = SdrObjKind::Rectangle; + } + break; + case ChartCommandID::DrawEllipse: + { + eDrawMode = CHARTDRAW_INSERT; + eKind = SdrObjKind::CircleOrEllipse; + } + break; + case ChartCommandID::DrawFreelineNoFill: + { + eDrawMode = CHARTDRAW_INSERT; + eKind = SdrObjKind::FreehandLine; + } + break; + case ChartCommandID::DrawText: + { + eDrawMode = CHARTDRAW_INSERT; + eKind = SdrObjKind::Text; + bCreate = true; + } + break; + case ChartCommandID::DrawCaption: + { + eDrawMode = CHARTDRAW_INSERT; + eKind = SdrObjKind::Caption; + } + break; + case ChartCommandID::DrawToolboxCsBasic: + case ChartCommandID::DrawToolboxCsSymbol: + case ChartCommandID::DrawToolboxCsArrow: + case ChartCommandID::DrawToolboxCsFlowchart: + case ChartCommandID::DrawToolboxCsCallout: + case ChartCommandID::DrawToolboxCsStar: + { + eDrawMode = CHARTDRAW_INSERT; + eKind = SdrObjKind::CustomShape; + } + break; + default: + { + eDrawMode = CHARTDRAW_SELECT; + eKind = SdrObjKind::NONE; + } + break; + } + + if ( !m_pChartController ) + return; + + DrawViewWrapper* pDrawViewWrapper = m_pChartController->GetDrawViewWrapper(); + if ( !pDrawViewWrapper ) + return; + + SolarMutexGuard aGuard; + m_pChartController->setDrawMode( eDrawMode ); + setInsertObj(eKind); + if ( bCreate ) + { + pDrawViewWrapper->SetCreateMode(); + } + + const beans::PropertyValue* pIter = rArgs.getConstArray(); + const beans::PropertyValue* pEnd = pIter + rArgs.getLength(); + const beans::PropertyValue* pKeyModifier = std::find_if(pIter, pEnd, + [](const beans::PropertyValue& lhs) + {return lhs.Name == "KeyModifier";} ); + sal_Int16 nKeyModifier = 0; + if ( !(pKeyModifier != pEnd && ( pKeyModifier->Value >>= nKeyModifier ) && nKeyModifier == KEY_MOD1) ) + return; + + if ( eDrawMode != CHARTDRAW_INSERT ) + return; + + rtl::Reference<SdrObject> pObj = createDefaultObject( nFeatureId ); + if ( pObj ) + { + SdrPageView* pPageView = pDrawViewWrapper->GetSdrPageView(); + if (pDrawViewWrapper->InsertObjectAtView(pObj.get(), *pPageView)) + m_pChartController->SetAndApplySelection(Reference<drawing::XShape>(pObj->getUnoShape(), uno::UNO_QUERY)); + if ( nFeatureId == ChartCommandID::DrawText ) + { + m_pChartController->StartTextEdit(); + } + } +} + +void DrawCommandDispatch::describeSupportedFeatures() +{ + implDescribeSupportedFeature( ".uno:SelectObject", ChartCommandID::DrawObjectSelect, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:Line", ChartCommandID::DrawLine, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:LineArrowEnd", ChartCommandID::DrawLineArrowEnd, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:Rect", ChartCommandID::DrawRect, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:Ellipse", ChartCommandID::DrawEllipse, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:Freeline_Unfilled", ChartCommandID::DrawFreelineNoFill, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DrawText", ChartCommandID::DrawText, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DrawCaption", ChartCommandID::DrawCaption, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:BasicShapes", ChartCommandID::DrawToolboxCsBasic, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:SymbolShapes", ChartCommandID::DrawToolboxCsSymbol, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:ArrowShapes", ChartCommandID::DrawToolboxCsArrow, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:FlowChartShapes", ChartCommandID::DrawToolboxCsFlowchart, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:CalloutShapes", ChartCommandID::DrawToolboxCsCallout, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:StarShapes", ChartCommandID::DrawToolboxCsStar, CommandGroup::INSERT ); +} + +void DrawCommandDispatch::setInsertObj(SdrObjKind eObj) +{ + DrawViewWrapper* pDrawViewWrapper = ( m_pChartController ? m_pChartController->GetDrawViewWrapper() : nullptr ); + if ( pDrawViewWrapper ) + { + pDrawViewWrapper->SetCurrentObj( eObj /*, Inventor */); + } +} + +rtl::Reference<SdrObject> DrawCommandDispatch::createDefaultObject( const ChartCommandID nID ) +{ + rtl::Reference<SdrObject> pObj; + DrawViewWrapper* pDrawViewWrapper = ( m_pChartController ? m_pChartController->GetDrawViewWrapper() : nullptr ); + DrawModelWrapper* pDrawModelWrapper = ( m_pChartController ? m_pChartController->GetDrawModelWrapper() : nullptr ); + + if ( pDrawViewWrapper && pDrawModelWrapper ) + { + Reference< drawing::XDrawPage > xDrawPage( pDrawModelWrapper->getMainDrawPage() ); + SdrPage* pPage = GetSdrPageFromXDrawPage( xDrawPage ); + if ( pPage ) + { + SolarMutexGuard aGuard; + + pObj = SdrObjFactory::MakeNewObject( + pDrawModelWrapper->getSdrModel(), + pDrawViewWrapper->GetCurrentObjInventor(), + pDrawViewWrapper->GetCurrentObjIdentifier()); + + if ( pObj ) + { + Size aObjectSize( 4000, 2500 ); + tools::Rectangle aPageRect( tools::Rectangle( Point( 0, 0 ), pPage->GetSize() ) ); + Point aObjectPos = aPageRect.Center(); + aObjectPos.AdjustX( -(aObjectSize.Width() / 2) ); + aObjectPos.AdjustY( -(aObjectSize.Height() / 2) ); + tools::Rectangle aRect( aObjectPos, aObjectSize ); + + switch ( nID ) + { + case ChartCommandID::DrawLine: + case ChartCommandID::DrawLineArrowEnd: + { + if ( auto const pathObj = dynamic_cast<SdrPathObj*>( pObj.get()) ) + { + Point aStart = aRect.TopLeft(); + Point aEnd = aRect.BottomRight(); + sal_Int32 nYMiddle( ( aRect.Top() + aRect.Bottom() ) / 2 ); + basegfx::B2DPolygon aPoly; + aPoly.append( basegfx::B2DPoint( aStart.X(), nYMiddle ) ); + aPoly.append( basegfx::B2DPoint( aEnd.X(), nYMiddle ) ); + pathObj->SetPathPoly(basegfx::B2DPolyPolygon(aPoly)); + SfxItemSet aSet( pDrawModelWrapper->GetItemPool() ); + setLineEnds( aSet ); + pObj->SetMergedItemSet( aSet ); + } + } + break; + case ChartCommandID::DrawFreelineNoFill: + { + if ( auto const pathObj = dynamic_cast<SdrPathObj*>( pObj.get()) ) + { + basegfx::B2DPolygon aInnerPoly; + aInnerPoly.append( basegfx::B2DPoint( aRect.Left(), aRect.Bottom() ) ); + aInnerPoly.appendBezierSegment( + basegfx::B2DPoint( aRect.Left(), aRect.Top() ), + basegfx::B2DPoint( aRect.Center().X(), aRect.Top() ), + basegfx::B2DPoint( aRect.Center().X(), aRect.Center().Y() ) ); + aInnerPoly.appendBezierSegment( + basegfx::B2DPoint( aRect.Center().X(), aRect.Bottom() ), + basegfx::B2DPoint( aRect.Right(), aRect.Bottom() ), + basegfx::B2DPoint( aRect.Right(), aRect.Top() ) ); + basegfx::B2DPolyPolygon aPoly; + aPoly.append( aInnerPoly ); + pathObj->SetPathPoly(aPoly); + } + } + break; + case ChartCommandID::DrawText: + case ChartCommandID::DrawTextVertical: + { + if ( SdrTextObj* pTextObj = DynCastSdrTextObj( pObj.get()) ) + { + pTextObj->SetLogicRect( aRect ); + bool bVertical = ( nID == ChartCommandID::DrawTextVertical ); + pTextObj->SetVerticalWriting( bVertical ); + if ( bVertical ) + { + SfxItemSet aSet( pDrawModelWrapper->GetItemPool() ); + aSet.Put( makeSdrTextAutoGrowWidthItem( true ) ); + aSet.Put( makeSdrTextAutoGrowHeightItem( false ) ); + aSet.Put( SdrTextVertAdjustItem( SDRTEXTVERTADJUST_TOP ) ); + aSet.Put( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_RIGHT ) ); + pTextObj->SetMergedItemSet( aSet ); + } + } + } + break; + case ChartCommandID::DrawCaption: + case ChartCommandID::DrawCaptionVertical: + { + if ( SdrCaptionObj* pCaptionObj = dynamic_cast<SdrCaptionObj*>( pObj.get()) ) + { + bool bIsVertical( nID == ChartCommandID::DrawCaptionVertical ); + pCaptionObj->SetVerticalWriting( bIsVertical ); + if ( bIsVertical ) + { + SfxItemSet aSet( pObj->GetMergedItemSet() ); + aSet.Put( SdrTextVertAdjustItem( SDRTEXTVERTADJUST_CENTER ) ); + aSet.Put( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_RIGHT ) ); + pObj->SetMergedItemSet( aSet ); + } + pCaptionObj->SetLogicRect( aRect ); + pCaptionObj->SetTailPos( + aRect.TopLeft() - Point( aRect.GetWidth() / 2, aRect.GetHeight() / 2 ) ); + } + } + break; + default: + { + pObj->SetLogicRect( aRect ); + SfxItemSet aSet( pDrawModelWrapper->GetItemPool() ); + setAttributes( pObj.get() ); + pObj->SetMergedItemSet( aSet ); + } + break; + } + } + } + } + + return pObj; +} + +bool DrawCommandDispatch::parseCommandURL( const OUString& rCommandURL, ChartCommandID* pnFeatureId, + OUString* pBaseCommand, OUString* pCustomShapeType ) +{ + bool bFound = true; + ChartCommandID nFeatureId = ChartCommandID::NONE; + OUString aBaseCommand; + OUString aType; + + sal_Int32 nIndex = std::min(sal_Int32(1), rCommandURL.getLength()); + std::u16string_view aToken = o3tl::getToken(rCommandURL, 0, '.', nIndex ); + if ( nIndex == -1 || aToken.empty() ) + { + aBaseCommand = rCommandURL; + SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.find( aBaseCommand ); + if ( aIter != m_aSupportedFeatures.end() ) + { + nFeatureId = aIter->second.nFeatureId; + + switch ( nFeatureId ) + { + case ChartCommandID::DrawToolboxCsBasic: + { + aType = "diamond"; + } + break; + case ChartCommandID::DrawToolboxCsSymbol: + { + aType = "smiley"; + } + break; + case ChartCommandID::DrawToolboxCsArrow: + { + aType = "left-right-arrow"; + } + break; + case ChartCommandID::DrawToolboxCsFlowchart: + { + aType = "flowchart-internal-storage"; + } + break; + case ChartCommandID::DrawToolboxCsCallout: + { + aType = "round-rectangular-callout"; + } + break; + case ChartCommandID::DrawToolboxCsStar: + { + aType = "star5"; + } + break; + default: + { + } + break; + } + } + else + { + bFound = false; + } + } + else + { + aBaseCommand = rCommandURL.copy( 0, nIndex - 1 ); + SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.find( aBaseCommand ); + if ( aIter != m_aSupportedFeatures.end() ) + { + nFeatureId = aIter->second.nFeatureId; + aType = rCommandURL.getToken( 0, '.', nIndex ); + } + else + { + bFound = false; + } + } + + *pnFeatureId = nFeatureId; + *pBaseCommand = aBaseCommand; + *pCustomShapeType = aType; + + return bFound; +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/DrawCommandDispatch.hxx b/chart2/source/controller/main/DrawCommandDispatch.hxx new file mode 100644 index 0000000000..65200cca63 --- /dev/null +++ b/chart2/source/controller/main/DrawCommandDispatch.hxx @@ -0,0 +1,74 @@ +/* -*- 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 . + */ +#pragma once + +#include <svx/svdobjkind.hxx> +#include "FeatureCommandDispatchBase.hxx" +#include <rtl/ref.hxx> + +class SfxItemSet; +class SdrObject; + +namespace chart +{ + +class ChartController; + +/** This is a CommandDispatch implementation for drawing objects. + */ +class DrawCommandDispatch: public FeatureCommandDispatchBase +{ +public: + DrawCommandDispatch( const css::uno::Reference< css::uno::XComponentContext >& rxContext, ChartController* pController ); + virtual ~DrawCommandDispatch() override; + + virtual bool isFeatureSupported( const OUString& rCommandURL ) override; + + void setAttributes( SdrObject* pObj ); + void setLineEnds( SfxItemSet& rAttr ); + +protected: + // WeakComponentImplHelperBase + virtual void disposing( std::unique_lock<std::mutex>& rGuard ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // state of a feature + virtual FeatureState getState( const OUString& rCommand ) override; + + // execute a feature + virtual void execute( const OUString& rCommand, const css::uno::Sequence< css::beans::PropertyValue>& rArgs ) override; + + // all the features which should be handled by this class + virtual void describeSupportedFeatures() override; + +private: + void setInsertObj(SdrObjKind eObj); + rtl::Reference<SdrObject> createDefaultObject( const ChartCommandID nID ); + + bool parseCommandURL( const OUString& rCommandURL, ChartCommandID* pnFeatureId, OUString* pBaseCommand, OUString* pCustomShapeType ); + + ChartController* m_pChartController; + OUString m_aCustomShapeType; +}; + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ElementSelector.cxx b/chart2/source/controller/main/ElementSelector.cxx new file mode 100644 index 0000000000..d538108ae8 --- /dev/null +++ b/chart2/source/controller/main/ElementSelector.cxx @@ -0,0 +1,319 @@ +/* -*- 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 "ElementSelector.hxx" +#include <ObjectNameProvider.hxx> +#include <ObjectHierarchy.hxx> +#include <servicenames.hxx> +#include <DrawViewWrapper.hxx> +#include <ResId.hxx> +#include <strings.hrc> +#include <ObjectIdentifier.hxx> +#include <ChartController.hxx> +#include <ChartModel.hxx> + +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/safeint.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/svapp.hxx> + +namespace chart { class ExplicitValueProvider; } + +namespace chart +{ + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace +{ +constexpr OUStringLiteral lcl_aServiceName + = u"com.sun.star.comp.chart.ElementSelectorToolbarController"; +} + +SelectorListBox::SelectorListBox(vcl::Window* pParent) + : InterimItemWindow(pParent, "modules/schart/ui/combobox.ui", "ComboBox") + , m_xWidget(m_xBuilder->weld_combo_box("combobox")) + , m_bReleaseFocus(true) +{ + InitControlBase(m_xWidget.get()); + + m_xWidget->connect_key_press(LINK(this, SelectorListBox, KeyInputHdl)); + m_xWidget->connect_changed(LINK(this, SelectorListBox, SelectHdl)); + m_xWidget->connect_focus_out(LINK(this, SelectorListBox, FocusOutHdl)); + + ::Size aLogicalSize(75, 0); + ::Size aPixelSize = LogicToPixel(aLogicalSize, MapMode(MapUnit::MapAppFont)); + m_xWidget->set_size_request(aPixelSize.Width(), -1); + SetSizePixel(m_xContainer->get_preferred_size()); +} + +void SelectorListBox::dispose() +{ + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +SelectorListBox::~SelectorListBox() +{ + disposeOnce(); +} + +static void lcl_addObjectsToList( const ObjectHierarchy& rHierarchy, const ObjectIdentifier & rParent, std::vector< ListBoxEntryData >& rEntries + , const sal_Int32 nHierarchyDepth, const rtl::Reference<::chart::ChartModel>& xChartDoc ) +{ + ObjectHierarchy::tChildContainer aChildren( rHierarchy.getChildren(rParent) ); + for (auto const& child : aChildren) + { + ListBoxEntryData aEntry; + aEntry.OID = child; + aEntry.UIName = ObjectNameProvider::getNameForCID( child.getObjectCID(), xChartDoc ); + aEntry.nHierarchyDepth = nHierarchyDepth; + rEntries.push_back(aEntry); + lcl_addObjectsToList( rHierarchy, child, rEntries, nHierarchyDepth+1, xChartDoc ); + } +} + +void SelectorListBox::SetChartController( const rtl::Reference< ::chart::ChartController >& xChartController ) +{ + m_xChartController = xChartController.get(); +} + +void SelectorListBox::UpdateChartElementsListAndSelection() +{ + m_xWidget->clear(); + m_aEntries.clear(); + + rtl::Reference< ::chart::ChartController > xChartController = m_xChartController.get(); + if( xChartController.is() ) + { + ObjectIdentifier aSelectedOID( xChartController->getSelection() ); + OUString aSelectedCID = aSelectedOID.getObjectCID(); + + rtl::Reference<::chart::ChartModel> xChartDoc = xChartController->getChartModel(); + ObjectType eType( aSelectedOID.getObjectType() ); + bool bAddSelectionToList = false; + if ( eType == OBJECTTYPE_DATA_POINT || eType == OBJECTTYPE_DATA_LABEL || eType == OBJECTTYPE_SHAPE ) + bAddSelectionToList = true; + + Reference< uno::XInterface > xChartView; + rtl::Reference< ChartModel > xFact = xChartController->getChartModel(); + if( xFact.is() ) + xChartView = xFact->createInstance( CHART_VIEW_SERVICE_NAME ); + ExplicitValueProvider* pExplicitValueProvider = nullptr; //ExplicitValueProvider::getExplicitValueProvider(xChartView); this creates all visible data points, that's too much + ObjectHierarchy aHierarchy( xChartDoc, pExplicitValueProvider, true /*bFlattenDiagram*/, true /*bOrderingForElementSelector*/ ); + lcl_addObjectsToList( aHierarchy, ::chart::ObjectHierarchy::getRootNodeOID(), m_aEntries, 0, xChartDoc ); + + if( bAddSelectionToList ) + { + if ( aSelectedOID.isAutoGeneratedObject() ) + { + OUString aSeriesCID = ObjectIdentifier::createClassifiedIdentifierForParticle( ObjectIdentifier::getSeriesParticleFromCID( aSelectedCID ) ); + std::vector< ListBoxEntryData >::iterator aIt = std::find_if(m_aEntries.begin(), m_aEntries.end(), + [&aSeriesCID](const ListBoxEntryData& rEntry) { return rEntry.OID.getObjectCID().match(aSeriesCID); }); + if (aIt != m_aEntries.end()) + { + ListBoxEntryData aEntry; + aEntry.UIName = ObjectNameProvider::getNameForCID( aSelectedCID, xChartDoc ); + aEntry.OID = aSelectedOID; + ++aIt; + if( aIt != m_aEntries.end() ) + m_aEntries.insert(aIt, aEntry); + else + m_aEntries.push_back( aEntry ); + } + } + else if ( aSelectedOID.isAdditionalShape() ) + { + ListBoxEntryData aEntry; + SdrObject* pSelectedObj = DrawViewWrapper::getSdrObject( aSelectedOID.getAdditionalShape() ); + OUString aName = pSelectedObj ? pSelectedObj->GetName() : OUString(); + aEntry.UIName = ( aName.isEmpty() ? SchResId( STR_OBJECT_SHAPE ) : aName ); + aEntry.OID = aSelectedOID; + m_aEntries.push_back( aEntry ); + } + } + + m_xWidget->freeze(); + sal_uInt16 nEntryPosToSelect = 0; bool bSelectionFound = false; + sal_uInt16 nN=0; + for (auto const& entry : m_aEntries) + { + // tdf#152087 strip any newlines from the entry + m_xWidget->append_text(entry.UIName.replaceAll("\n", " ")); + if ( !bSelectionFound && aSelectedOID == entry.OID ) + { + nEntryPosToSelect = nN; + bSelectionFound = true; + } + ++nN; + } + m_xWidget->thaw(); + + if( bSelectionFound ) + m_xWidget->set_active(nEntryPosToSelect); + } + m_xWidget->save_value(); //remind current selection pos +} + +void SelectorListBox::ReleaseFocus_Impl() +{ + if ( !m_bReleaseFocus ) + { + m_bReleaseFocus = true; + return; + } + + rtl::Reference< ::chart::ChartController > xController = m_xChartController.get(); + Reference< frame::XFrame > xFrame( xController->getFrame() ); + if ( xFrame.is() && xFrame->getContainerWindow().is() ) + xFrame->getContainerWindow()->setFocus(); +} + +IMPL_LINK(SelectorListBox, SelectHdl, weld::ComboBox&, rComboBox, void) +{ + if (rComboBox.changed_by_direct_pick()) + { + const sal_Int32 nPos = rComboBox.get_active(); + if (o3tl::make_unsigned(nPos) < m_aEntries.size()) + { + ObjectIdentifier aOID = m_aEntries[nPos].OID; + rtl::Reference< ::chart::ChartController > xController = m_xChartController.get(); + if( xController.is() ) + xController->select( aOID.getAny() ); + } + ReleaseFocus_Impl(); + } +} + +IMPL_LINK(SelectorListBox, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + bool bHandled = false; + + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + + switch ( nCode ) + { + case KEY_RETURN: + case KEY_TAB: + { + if ( nCode == KEY_TAB ) + m_bReleaseFocus = false; + else + bHandled = true; + SelectHdl(*m_xWidget); + break; + } + + case KEY_ESCAPE: + m_xWidget->set_active_text(m_xWidget->get_saved_value()); //restore saved selection + ReleaseFocus_Impl(); + break; + } + + return bHandled || ChildKeyInput(rKEvt); +} + +IMPL_LINK_NOARG(SelectorListBox, FocusOutHdl, weld::Widget&, void) +{ + if (m_xWidget && !m_xWidget->has_focus()) // comboboxes can be comprised of multiple widgets, ensure all have lost focus + m_xWidget->set_active_text(m_xWidget->get_saved_value()); +} + +OUString SAL_CALL ElementSelectorToolbarController::getImplementationName() +{ + return lcl_aServiceName; +} + +sal_Bool SAL_CALL ElementSelectorToolbarController::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL ElementSelectorToolbarController::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ToolbarController" }; +} +ElementSelectorToolbarController::ElementSelectorToolbarController() +{ +} +ElementSelectorToolbarController::~ElementSelectorToolbarController() +{ +} +// XInterface +Any SAL_CALL ElementSelectorToolbarController::queryInterface( const Type& _rType ) +{ + Any aReturn = ToolboxController::queryInterface(_rType); + if (!aReturn.hasValue()) + aReturn = ElementSelectorToolbarController_BASE::queryInterface(_rType); + return aReturn; +} +void SAL_CALL ElementSelectorToolbarController::acquire() noexcept +{ + ToolboxController::acquire(); +} +void SAL_CALL ElementSelectorToolbarController::release() noexcept +{ + ToolboxController::release(); +} +void SAL_CALL ElementSelectorToolbarController::statusChanged( const frame::FeatureStateEvent& rEvent ) +{ + if( m_apSelectorListBox ) + { + SolarMutexGuard aSolarMutexGuard; + if ( rEvent.FeatureURL.Path == "ChartElementSelector" ) + { + Reference< frame::XController > xChartController; + rEvent.State >>= xChartController; + ::chart::ChartController* pController = dynamic_cast<::chart::ChartController*>(xChartController.get()); + assert(!xChartController || pController); + m_apSelectorListBox->SetChartController( pController ); + m_apSelectorListBox->UpdateChartElementsListAndSelection(); + } + } +} +uno::Reference< awt::XWindow > SAL_CALL ElementSelectorToolbarController::createItemWindow( const uno::Reference< awt::XWindow >& xParent ) +{ + uno::Reference< awt::XWindow > xItemWindow; + if( !m_apSelectorListBox ) + { + VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow( xParent ); + if( pParent ) + { + m_apSelectorListBox.reset(VclPtr<SelectorListBox>::Create(pParent)); + } + } + if( m_apSelectorListBox ) + xItemWindow = VCLUnoHelper::GetInterface( m_apSelectorListBox.get() ); + return xItemWindow; +} + +} // chart2 + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_chart_ElementSelectorToolbarController_get_implementation(css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new chart::ElementSelectorToolbarController ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ElementSelector.hxx b/chart2/source/controller/main/ElementSelector.hxx new file mode 100644 index 0000000000..fb1e4e0527 --- /dev/null +++ b/chart2/source/controller/main/ElementSelector.hxx @@ -0,0 +1,99 @@ +/* -*- 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 . + */ +#pragma once + +#include <ObjectIdentifier.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase1.hxx> +#include <svtools/toolboxcontroller.hxx> + +#include <vcl/InterimItemWindow.hxx> +#include <unotools/weakref.hxx> + +namespace chart +{ +class ChartController; + +struct ListBoxEntryData +{ + OUString UIName; + ObjectIdentifier OID; + sal_Int32 nHierarchyDepth; + + ListBoxEntryData() : nHierarchyDepth(0) + { + } +}; + +class SelectorListBox final : public InterimItemWindow +{ +public: + SelectorListBox(vcl::Window* pParent); + virtual void dispose() override; + virtual ~SelectorListBox() override; + + void ReleaseFocus_Impl(); + + void SetChartController( const rtl::Reference< ::chart::ChartController >& xChartController ); + void UpdateChartElementsListAndSelection(); + +private: + unotools::WeakReference<::chart::ChartController> m_xChartController; + std::unique_ptr<weld::ComboBox> m_xWidget; + + std::vector<ListBoxEntryData> m_aEntries; + + bool m_bReleaseFocus; + + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(SelectHdl, weld::ComboBox&, void); + DECL_LINK(FocusOutHdl, weld::Widget&, void); +}; + +typedef ::cppu::ImplHelper1 < css::lang::XServiceInfo> ElementSelectorToolbarController_BASE; + +class ElementSelectorToolbarController : public ::svt::ToolboxController + , public ElementSelectorToolbarController_BASE +{ +public: + explicit ElementSelectorToolbarController(); + virtual ~ElementSelectorToolbarController() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + // XToolbarController + virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createItemWindow( const css::uno::Reference< css::awt::XWindow >& Parent ) override; + +private: + VclPtr< SelectorListBox > m_apSelectorListBox; +}; + +} //namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/FeatureCommandDispatchBase.cxx b/chart2/source/controller/main/FeatureCommandDispatchBase.cxx new file mode 100644 index 0000000000..b1c5f72d1d --- /dev/null +++ b/chart2/source/controller/main/FeatureCommandDispatchBase.cxx @@ -0,0 +1,94 @@ +/* -*- 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 "FeatureCommandDispatchBase.hxx" + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace chart +{ + +FeatureCommandDispatchBase::FeatureCommandDispatchBase( const Reference< uno::XComponentContext >& rxContext ) + :CommandDispatch( rxContext ) + ,m_nFeatureId( ChartCommandID::NONE ) +{ +} + +FeatureCommandDispatchBase::~FeatureCommandDispatchBase() +{ +} + +void FeatureCommandDispatchBase::initialize() +{ + CommandDispatch::initialize(); + describeSupportedFeatures(); +} + +bool FeatureCommandDispatchBase::isFeatureSupported( const OUString& rCommandURL ) +{ + SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.find( rCommandURL ); + return aIter != m_aSupportedFeatures.end(); +} + +void FeatureCommandDispatchBase::fireStatusEvent( const OUString& rURL, + const Reference< frame::XStatusListener >& xSingleListener /* = 0 */ ) +{ + if ( rURL.isEmpty() ) + { + for (auto const& elem : m_aSupportedFeatures) + { + FeatureState aFeatureState( getState(elem.first) ); + fireStatusEventForURL( elem.first, aFeatureState.aState, aFeatureState.bEnabled, xSingleListener ); + } + } + else + { + FeatureState aFeatureState( getState( rURL ) ); + fireStatusEventForURL( rURL, aFeatureState.aState, aFeatureState.bEnabled, xSingleListener ); + } +} + +// XDispatch +void FeatureCommandDispatchBase::dispatch( const util::URL& URL, + const Sequence< beans::PropertyValue >& Arguments ) +{ + OUString aCommand( URL.Complete ); + if ( getState( aCommand ).bEnabled ) + { + execute( aCommand, Arguments ); + } +} + +void FeatureCommandDispatchBase::implDescribeSupportedFeature( const char* pAsciiCommandURL, + ChartCommandID nId, sal_Int16 nGroup ) +{ + ControllerFeature aFeature; + aFeature.Command = OUString::createFromAscii( pAsciiCommandURL ); + aFeature.nFeatureId = nId; + aFeature.GroupId = nGroup; + + m_aSupportedFeatures[ aFeature.Command ] = aFeature; +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/FeatureCommandDispatchBase.hxx b/chart2/source/controller/main/FeatureCommandDispatchBase.hxx new file mode 100644 index 0000000000..3ceb35ad12 --- /dev/null +++ b/chart2/source/controller/main/FeatureCommandDispatchBase.hxx @@ -0,0 +1,135 @@ +/* -*- 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 . + */ +#pragma once + +#include "CommandDispatch.hxx" + +#include <com/sun/star/frame/DispatchInformation.hpp> + +enum class ChartCommandID +{ + NONE = 0, + + //Draw Command Ids: + DrawObjectSelect = 1, + DrawLine = 2, + DrawLineArrowEnd = 3, + DrawRect = 4, + DrawEllipse = 5, + DrawFreelineNoFill = 6, + DrawText = 7, + DrawTextVertical = 8, + DrawCaption = 9, + DrawCaptionVertical = 10, + DrawToolboxCsBasic = 11, + DrawToolboxCsSymbol = 12, + DrawToolboxCsArrow = 13, + DrawToolboxCsFlowchart = 14, + DrawToolboxCsCallout = 15, + DrawToolboxCsStar = 16, + + //Shape Controller Command Ids: + ShapeFormatLine = 21, + ShapeFormatArea = 22, + ShapeTextAttributes = 23, + ShapeTransformDialog = 24, + ShapeObjectTitleDescription = 25, + ShapeRenameObject = 26, + ShapeBringToFront = 28, + ShapeForward = 29, + ShapeBackward = 30, + ShapeSendToBack = 31, + ShapeFontDialog = 35, + ShapeParagraphDialog = 36 +}; + + +namespace chart +{ + +struct ControllerFeature: public css::frame::DispatchInformation +{ + ChartCommandID nFeatureId; +}; + +typedef std::map< OUString, + ControllerFeature > SupportedFeatures; + +struct FeatureState +{ + bool bEnabled; + css::uno::Any aState; + + FeatureState() : bEnabled( false ) { } +}; + +/** This is a base class for CommandDispatch implementations with feature support. + */ +class FeatureCommandDispatchBase: public CommandDispatch +{ +public: + explicit FeatureCommandDispatchBase( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~FeatureCommandDispatchBase() override; + + // late initialisation, especially for adding as listener + virtual void initialize() override; + + virtual bool isFeatureSupported( const OUString& rCommandURL ); + +protected: + // XDispatch + virtual void SAL_CALL dispatch( const css::util::URL& URL, + const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + + virtual void fireStatusEvent( const OUString& rURL, + const css::uno::Reference< css::frame::XStatusListener >& xSingleListener ) override; + + // state of a feature + virtual FeatureState getState( const OUString& rCommand ) = 0; + + // execute a feature + virtual void execute( const OUString& rCommand, const css::uno::Sequence< css::beans::PropertyValue>& rArgs ) = 0; + + // all the features which should be handled by this class + virtual void describeSupportedFeatures() = 0; + + /** describes a feature supported by the controller + + Must not be called outside <member>describeSupportedFeatures</member>. + + @param pAsciiCommandURL + the URL of the feature command + @param nId + the id of the feature. Later references to this feature usually happen by id, not by + URL. + @param nGroup + the command group of the feature. This is important for configuring the controller UI + by the user, see also <type scope="css::frame">CommandGroup</type>. + */ + void implDescribeSupportedFeature( const char* pAsciiCommandURL, ChartCommandID nId, + sal_Int16 nGroup ); + + mutable SupportedFeatures m_aSupportedFeatures; + + ChartCommandID m_nFeatureId; +}; + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ObjectHierarchy.cxx b/chart2/source/controller/main/ObjectHierarchy.cxx new file mode 100644 index 0000000000..44846850dc --- /dev/null +++ b/chart2/source/controller/main/ObjectHierarchy.cxx @@ -0,0 +1,727 @@ +/* -*- 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 <ObjectHierarchy.hxx> +#include <ObjectIdentifier.hxx> +#include <ChartModelHelper.hxx> +#include <DiagramHelper.hxx> +#include <Diagram.hxx> +#include <RegressionCurveHelper.hxx> +#include <RegressionCurveModel.hxx> +#include <Axis.hxx> +#include <AxisHelper.hxx> +#include <chartview/ExplicitValueProvider.hxx> +#include <ChartType.hxx> +#include <ChartTypeHelper.hxx> +#include <ChartModel.hxx> +#include <DataSeries.hxx> +#include <DataSeriesHelper.hxx> +#include <GridProperties.hxx> +#include <LegendHelper.hxx> +#include <chartview/DrawModelWrapper.hxx> +#include <unonames.hxx> +#include <BaseCoordinateSystem.hxx> + +#include <map> +#include <algorithm> +#include <cstddef> + +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/chart/ErrorBarStyle.hpp> + +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/awt/Key.hpp> +#include <com/sun/star/awt/KeyModifier.hpp> +#include <utility> +#include <comphelper/diagnose_ex.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace +{ + +void lcl_getChildOIDs( + ::chart::ObjectHierarchy::tChildContainer& rOutChildren, + const Reference< container::XIndexAccess >& xShapes ) +{ + if( !xShapes.is()) + return; + + sal_Int32 nCount = xShapes->getCount(); + for( sal_Int32 i=0; i<nCount; ++i) + { + Reference< beans::XPropertySet > xShapeProp( xShapes->getByIndex( i ), uno::UNO_QUERY ); + if( xShapeProp.is()) + { + Reference< beans::XPropertySetInfo > xInfo( xShapeProp->getPropertySetInfo()); + OUString aName; + if( xInfo.is() && + xInfo->hasPropertyByName( "Name") && + (xShapeProp->getPropertyValue( "Name") >>= aName ) && + !aName.isEmpty() && + ::chart::ObjectIdentifier::isCID( aName )) + { + rOutChildren.emplace_back( aName ); + } + Reference< container::XIndexAccess > xNewShapes( xShapeProp, uno::UNO_QUERY ); + if( xNewShapes.is()) + lcl_getChildOIDs( rOutChildren, xNewShapes ); + } + } +} + +void lcl_addAxisTitle( const rtl::Reference< ::chart::Axis >& xAxis, ::chart::ObjectHierarchy::tChildContainer& rContainer, const rtl::Reference<::chart::ChartModel>& xChartModel ) +{ + if( xAxis.is()) + { + Reference< XTitle > xAxisTitle( xAxis->getTitleObject()); + if( xAxisTitle.is()) + rContainer.emplace_back( ::chart::ObjectIdentifier::createClassifiedIdentifierForObject( xAxisTitle, xChartModel ) ); + } +} + +} // anonymous namespace + +namespace chart +{ + +void ObjectHierarchy::createTree( const rtl::Reference<::chart::ChartModel>& xChartDocument ) +{ + m_aChildMap.clear(); + + if( !xChartDocument.is() ) + return; + + //@todo: change ObjectIdentifier to take an XChartDocument rather than XModel + rtl::Reference< Diagram > xDiagram = xChartDocument->getFirstChartDiagram(); + ObjectIdentifier aDiaOID; + if( xDiagram.is() ) + aDiaOID = ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForObject( static_cast<cppu::OWeakObject*>(xDiagram.get()), xChartDocument ) ); + tChildContainer aTopLevelContainer; + + // First Level + + // Chart Area + if( m_bOrderingForElementSelector ) + { + aTopLevelContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, u"" ) ); + if( xDiagram.is() ) + { + aTopLevelContainer.push_back( aDiaOID ); + createWallAndFloor( aTopLevelContainer, xDiagram ); + createLegendTree( aTopLevelContainer, xChartDocument, xDiagram ); + } + } + + // Main Title + Reference< XTitle > xMainTitle( xChartDocument->getTitleObject()); + if( xMainTitle.is()) + aTopLevelContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForObject( xMainTitle, xChartDocument ) ); + + if( xDiagram.is()) + { + // Sub Title. Note: This is interpreted of being top level + Reference< XTitle > xSubTitle( xDiagram->getTitleObject()); + if( xSubTitle.is()) + aTopLevelContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForObject( xSubTitle, xChartDocument ) ); + + if( !m_bOrderingForElementSelector ) + { + // Axis Titles. Note: These are interpreted of being top level + const std::vector< rtl::Reference< Axis > > aAxes = AxisHelper::getAllAxesOfDiagram( xDiagram ); + for( rtl::Reference< Axis > const & axis : aAxes ) + lcl_addAxisTitle( axis, aTopLevelContainer, xChartDocument ); + + // Diagram + aTopLevelContainer.push_back( aDiaOID ); + } + + if( m_bFlattenDiagram ) + createDiagramTree( aTopLevelContainer, xChartDocument, xDiagram ); + else + { + tChildContainer aSubContainer; + createDiagramTree( aSubContainer, xChartDocument, xDiagram ); + if( !aSubContainer.empty() ) + m_aChildMap[ aDiaOID ] = aSubContainer; + } + + if( !m_bOrderingForElementSelector ) + createLegendTree( aTopLevelContainer, xChartDocument, xDiagram ); + } + + // #i12587# support for shapes in chart + if ( !m_bOrderingForElementSelector ) + { + createAdditionalShapesTree( aTopLevelContainer ); + } + + // Chart Area + if( !m_bOrderingForElementSelector ) + aTopLevelContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, u"" ) ); + + if( ! aTopLevelContainer.empty()) + m_aChildMap[ObjectHierarchy::getRootNodeOID()] = aTopLevelContainer; +} + +void ObjectHierarchy::createLegendTree( + tChildContainer & rContainer, + const rtl::Reference<::chart::ChartModel> & xChartDoc, + const rtl::Reference< Diagram > & xDiagram ) +{ + if( !(xDiagram.is() && LegendHelper::hasLegend( xDiagram )) ) + return; + + ObjectIdentifier aLegendOID( ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForObject( xDiagram->getLegend(), xChartDoc ) ) ); + rContainer.push_back( aLegendOID ); + + // iterate over child shapes of legend and search for matching CIDs + if( m_pExplicitValueProvider ) + { + rtl::Reference< SvxShapeGroupAnyD > xLegendShapeContainer = + dynamic_cast<SvxShapeGroupAnyD*>( + m_pExplicitValueProvider->getShapeForCID( aLegendOID.getObjectCID() ).get() ); + tChildContainer aLegendEntryOIDs; + lcl_getChildOIDs( aLegendEntryOIDs, xLegendShapeContainer ); + + m_aChildMap[ aLegendOID ] = aLegendEntryOIDs; + } +} + +void ObjectHierarchy::createAxesTree( + tChildContainer & rContainer, + const rtl::Reference<::chart::ChartModel> & xChartDoc, + const rtl::Reference< Diagram > & xDiagram ) +{ + sal_Int32 nDimensionCount = xDiagram->getDimension(); + rtl::Reference< ChartType > xChartType( xDiagram->getChartTypeByIndex( 0 ) ); + bool bSupportsAxesGrids = ChartTypeHelper::isSupportingMainAxis( xChartType, nDimensionCount, 0 ); + if( !bSupportsAxesGrids ) + return; + + // Data Table + uno::Reference<chart2::XDataTable> xDataTable = xDiagram->getDataTable(); + if (xDataTable.is()) + { + rContainer.push_back(ObjectIdentifier::createClassifiedIdentifierForObject(xDataTable, xChartDoc)); + } + + // Axes + std::vector< rtl::Reference< Axis > > aAxes = AxisHelper::getAllAxesOfDiagram( xDiagram, /* bOnlyVisible = */ true ); + if( !m_bOrderingForElementSelector ) + { + for (const auto & rAxis : std::as_const(aAxes)) + rContainer.push_back( ObjectIdentifier::createClassifiedIdentifierForObject( rAxis, xChartDoc ) ); + } + + // get all axes, also invisible ones + aAxes = AxisHelper::getAllAxesOfDiagram( xDiagram ); + // Grids + for( rtl::Reference< Axis > const & xAxis : aAxes ) + { + if(!xAxis.is()) + continue; + + sal_Int32 nCooSysIndex = 0; + sal_Int32 nDimensionIndex = 0; + sal_Int32 nAxisIndex = 0; + AxisHelper::getIndicesForAxis( xAxis, xDiagram, nCooSysIndex, nDimensionIndex, nAxisIndex ); + if( nAxisIndex>0 && !ChartTypeHelper::isSupportingSecondaryAxis( xChartType, nDimensionCount ) ) + continue; + + if( m_bOrderingForElementSelector ) + { + // axis + if( AxisHelper::isAxisVisible( xAxis ) ) + rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForObject( xAxis, xChartDoc ) ); + + // axis title + lcl_addAxisTitle( xAxis, rContainer, xChartDoc ); + } + + rtl::Reference< ::chart::GridProperties > xGridProperties( xAxis->getGridProperties2() ); + if( AxisHelper::isGridVisible( xGridProperties ) ) + { + //main grid + rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForGrid( xAxis, xChartDoc ) ); + } + + std::vector< rtl::Reference< ::chart::GridProperties > > aSubGrids( xAxis->getSubGridProperties2() ); + for( size_t nSubGrid = 0; nSubGrid < aSubGrids.size(); ++nSubGrid ) + { + if( AxisHelper::isGridVisible( aSubGrids[nSubGrid] ) ) + { + //sub grid + rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForGrid( xAxis, xChartDoc, nSubGrid ) ); + } + } + } +} + +void ObjectHierarchy::createWallAndFloor( + tChildContainer & rContainer, + const rtl::Reference< Diagram > & xDiagram ) +{ + sal_Int32 nDimensionCount = xDiagram->getDimension(); + bool bIsThreeD = ( nDimensionCount == 3 ); + bool bHasWall = xDiagram->isSupportingFloorAndWall(); + if( bHasWall && bIsThreeD ) + { + rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL, u"" ) ); + + Reference< beans::XPropertySet > xFloor( xDiagram->getFloor()); + if( xFloor.is()) + rContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_FLOOR, u"" ) ); + } + +} + +void ObjectHierarchy::createDiagramTree( + tChildContainer & rContainer, + const rtl::Reference<::chart::ChartModel> & xChartDoc, + const rtl::Reference< Diagram > & xDiagram ) +{ + if( !m_bOrderingForElementSelector ) + { + createDataSeriesTree( rContainer, xDiagram ); + createAxesTree( rContainer, xChartDoc, xDiagram ); + createWallAndFloor( rContainer, xDiagram ); + } + else + { + createAxesTree( rContainer, xChartDoc, xDiagram ); + createDataSeriesTree( rContainer, xDiagram ); + } +} + +void ObjectHierarchy::createDataSeriesTree( + tChildContainer & rOutDiagramSubContainer, + const rtl::Reference< Diagram > & xDiagram ) +{ + try + { + sal_Int32 nDimensionCount = xDiagram->getDimension(); + std::vector< rtl::Reference< BaseCoordinateSystem > > aCooSysSeq( + xDiagram->getBaseCoordinateSystems()); + for( std::size_t nCooSysIdx=0; nCooSysIdx<aCooSysSeq.size(); ++nCooSysIdx ) + { + std::vector< rtl::Reference< ChartType > > aChartTypeSeq( aCooSysSeq[nCooSysIdx]->getChartTypes2()); + for( std::size_t nCTIdx=0; nCTIdx<aChartTypeSeq.size(); ++nCTIdx ) + { + rtl::Reference< ChartType > xChartType( aChartTypeSeq[nCTIdx] ); + std::vector< rtl::Reference< DataSeries > > aSeriesSeq( xChartType->getDataSeries2() ); + const sal_Int32 nNumberOfSeries = + ChartTypeHelper::getNumberOfDisplayedSeries( xChartType, aSeriesSeq.size()); + + for( sal_Int32 nSeriesIdx=0; nSeriesIdx<nNumberOfSeries; ++nSeriesIdx ) + { + OUString aSeriesParticle( + ObjectIdentifier::createParticleForSeries( + 0, nCooSysIdx, nCTIdx, nSeriesIdx )); + ObjectIdentifier aSeriesOID( + ObjectIdentifier( ObjectIdentifier::createClassifiedIdentifierForParticle( aSeriesParticle ) ) ); + rOutDiagramSubContainer.push_back( aSeriesOID ); + + tChildContainer aSeriesSubContainer; + + rtl::Reference< DataSeries > const & xSeries = aSeriesSeq[nSeriesIdx]; + + // data labels + if( DataSeriesHelper::hasDataLabelsAtSeries( xSeries ) ) + { + OUString aChildParticle( ObjectIdentifier::getStringForType( OBJECTTYPE_DATA_LABELS ) + "=" ); + aSeriesSubContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierForParticles( aSeriesParticle, aChildParticle ) ); + } + + // Statistics + if( ChartTypeHelper::isSupportingStatisticProperties( xChartType, nDimensionCount ) ) + { + const std::vector< rtl::Reference< RegressionCurveModel > > & rCurves( xSeries->getRegressionCurves2()); + for( size_t nCurveIdx=0; nCurveIdx<rCurves.size(); ++nCurveIdx ) + { + bool bIsAverageLine = RegressionCurveHelper::isMeanValueLine( rCurves[nCurveIdx] ); + aSeriesSubContainer.emplace_back( ObjectIdentifier::createDataCurveCID( aSeriesParticle, nCurveIdx, bIsAverageLine ) ); + if( RegressionCurveHelper::hasEquation( rCurves[nCurveIdx] ) ) + { + aSeriesSubContainer.emplace_back( ObjectIdentifier::createDataCurveEquationCID( aSeriesParticle, nCurveIdx ) ); + } + } + Reference< beans::XPropertySet > xErrorBarProp; + if( (xSeries->getPropertyValue( CHART_UNONAME_ERRORBAR_Y) >>= xErrorBarProp) && + xErrorBarProp.is()) + { + sal_Int32 nStyle = css::chart::ErrorBarStyle::NONE; + if( ( xErrorBarProp->getPropertyValue( "ErrorBarStyle") >>= nStyle ) && + ( nStyle != css::chart::ErrorBarStyle::NONE ) ) + { + aSeriesSubContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierWithParent( + OBJECTTYPE_DATA_ERRORS_Y, u"", aSeriesParticle ) ); + } + } + + if( (xSeries->getPropertyValue(CHART_UNONAME_ERRORBAR_X) >>= xErrorBarProp) && + xErrorBarProp.is()) + { + sal_Int32 nStyle = css::chart::ErrorBarStyle::NONE; + if( ( xErrorBarProp->getPropertyValue( "ErrorBarStyle") >>= nStyle ) && + ( nStyle != css::chart::ErrorBarStyle::NONE ) ) + { + aSeriesSubContainer.emplace_back( ObjectIdentifier::createClassifiedIdentifierWithParent( + OBJECTTYPE_DATA_ERRORS_X, u"", aSeriesParticle ) ); + } + } + } + + // Data Points + // iterate over child shapes of legend and search for matching CIDs + if( m_pExplicitValueProvider ) + { + rtl::Reference< SvxShapeGroupAnyD > xSeriesShapeContainer = + dynamic_cast<SvxShapeGroupAnyD*>( + m_pExplicitValueProvider->getShapeForCID( aSeriesOID.getObjectCID() ).get() ); + lcl_getChildOIDs( aSeriesSubContainer, xSeriesShapeContainer ); + } + + if( ! aSeriesSubContainer.empty()) + m_aChildMap[ aSeriesOID ] = aSeriesSubContainer; + } + } + } + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } +} + +void ObjectHierarchy::createAdditionalShapesTree(tChildContainer& rContainer) +{ + try + { + if ( m_pExplicitValueProvider ) + { + rtl::Reference<SvxDrawPage> xDrawPage( m_pExplicitValueProvider->getDrawModelWrapper()->getMainDrawPage() ); + Reference< drawing::XShapes > xChartRoot( DrawModelWrapper::getChartRootShape( xDrawPage ) ); + sal_Int32 nCount = xDrawPage->getCount(); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + Reference< drawing::XShape > xShape; + if ( xDrawPage->getByIndex( i ) >>= xShape ) + { + if ( xShape.is() && xShape != xChartRoot ) + { + rContainer.emplace_back( xShape ); + } + } + } + } + } + catch ( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } +} + +bool ObjectHierarchy::hasChildren( const ObjectIdentifier& rParent ) const +{ + if ( rParent.isValid() ) + { + tChildMap::const_iterator aIt( m_aChildMap.find( rParent )); + if( aIt != m_aChildMap.end()) + return ! (aIt->second.empty()); + } + return false; +} + +const ObjectHierarchy::tChildContainer & ObjectHierarchy::getChildren( const ObjectIdentifier& rParent ) const +{ + if ( rParent.isValid() ) + { + tChildMap::const_iterator aIt( m_aChildMap.find( rParent )); + if( aIt != m_aChildMap.end()) + return aIt->second; + } + static const tChildContainer EMPTY; + return EMPTY; +} + +const ObjectHierarchy::tChildContainer & ObjectHierarchy::getSiblings( const ObjectIdentifier& rNode ) const +{ + if ( rNode.isValid() && !ObjectHierarchy::isRootNode( rNode ) ) + { + for (auto const& child : m_aChildMap) + { + tChildContainer::const_iterator aElemIt( + std::find( child.second.begin(), child.second.end(), rNode )); + if( aElemIt != child.second.end()) + return child.second; + } + } + static const tChildContainer EMPTY; + return EMPTY; +} + +ObjectIdentifier ObjectHierarchy::getParentImpl( + const ObjectIdentifier & rParentOID, + const ObjectIdentifier & rOID ) const +{ + // search children + tChildContainer aChildren( getChildren( rParentOID )); + tChildContainer::const_iterator aIt( + std::find( aChildren.begin(), aChildren.end(), rOID )); + // recursion end + if( aIt != aChildren.end()) + return rParentOID; + + for (auto const& child : aChildren) + { + // recursion + ObjectIdentifier aTempParent( getParentImpl( child, rOID )); + if ( aTempParent.isValid() ) + { + // exit on success + return aTempParent; + } + } + + // exit on fail + return ObjectIdentifier(); +} + +ObjectIdentifier ObjectHierarchy::getParent( + const ObjectIdentifier & rOID ) const +{ + return getParentImpl( ObjectHierarchy::getRootNodeOID(), rOID ); +} + +ObjectHierarchy::ObjectHierarchy( + const rtl::Reference<::chart::ChartModel> & xChartDocument, + ExplicitValueProvider * pExplicitValueProvider /* = 0 */, + bool bFlattenDiagram /* = false */, + bool bOrderingForElementSelector /* = false */) : + m_pExplicitValueProvider( pExplicitValueProvider ), + m_bFlattenDiagram( bFlattenDiagram ), + m_bOrderingForElementSelector( bOrderingForElementSelector ) +{ + createTree( xChartDocument ); + // don't remember this helper to avoid access after lifetime + m_pExplicitValueProvider = nullptr; +} + +ObjectHierarchy::~ObjectHierarchy() +{} + +ObjectIdentifier ObjectHierarchy::getRootNodeOID() +{ + return ObjectIdentifier( "ROOT" ); +} + +bool ObjectHierarchy::isRootNode( const ObjectIdentifier& rOID ) +{ + return ( rOID == ObjectHierarchy::getRootNodeOID() ); +} + +const ObjectHierarchy::tChildContainer & ObjectHierarchy::getTopLevelChildren() const +{ + return getChildren( ObjectHierarchy::getRootNodeOID()); +} + +sal_Int32 ObjectHierarchy::getIndexInParent( + const ObjectIdentifier& rNode ) const +{ + ObjectIdentifier aParentOID( getParent( rNode )); + const tChildContainer & aChildren( getChildren( aParentOID ) ); + sal_Int32 nIndex = 0; + for (auto const& child : aChildren) + { + if ( child == rNode ) + return nIndex; + ++nIndex; + } + return -1; +} + +ObjectKeyNavigation::ObjectKeyNavigation( + ObjectIdentifier aCurrentOID, + rtl::Reference<::chart::ChartModel> xChartDocument, + ExplicitValueProvider * pExplicitValueProvider /* = 0 */ ) : + m_aCurrentOID(std::move( aCurrentOID )), + m_xChartDocument(std::move( xChartDocument )), + m_pExplicitValueProvider( pExplicitValueProvider ) +{ + if ( !m_aCurrentOID.isValid() ) + { + setCurrentSelection( ObjectHierarchy::getRootNodeOID() ); + } +} + +bool ObjectKeyNavigation::handleKeyEvent( + const awt::KeyEvent & rEvent ) +{ + bool bResult = false; + + switch( rEvent.KeyCode ) + { + case awt::Key::TAB: + if( rEvent.Modifiers & awt::KeyModifier::SHIFT ) + bResult = previous(); + else + bResult = next(); + break; + case awt::Key::HOME: + bResult = first(); + break; + case awt::Key::END: + bResult = last(); + break; + case awt::Key::F3: + if( rEvent.Modifiers & awt::KeyModifier::SHIFT ) + bResult = up(); + else + bResult = down(); + break; + case awt::Key::ESCAPE: + setCurrentSelection( ObjectIdentifier() ); + bResult = true; + break; + default: + bResult = false; + break; + } + return bResult; +} + +void ObjectKeyNavigation::setCurrentSelection( const ObjectIdentifier& rOID ) +{ + m_aCurrentOID = rOID; +} + +bool ObjectKeyNavigation::first() +{ + ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider ); + ObjectHierarchy::tChildContainer aSiblings( aHierarchy.getSiblings( getCurrentSelection() ) ); + bool bResult = !aSiblings.empty(); + if( bResult ) + setCurrentSelection( aSiblings.front()); + else + bResult = veryFirst(); + return bResult; +} + +bool ObjectKeyNavigation::last() +{ + ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider ); + ObjectHierarchy::tChildContainer aSiblings( aHierarchy.getSiblings( getCurrentSelection() ) ); + bool bResult = !aSiblings.empty(); + if( bResult ) + setCurrentSelection( aSiblings.back()); + else + bResult = veryLast(); + return bResult; +} + +bool ObjectKeyNavigation::next() +{ + ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider ); + ObjectHierarchy::tChildContainer aSiblings( aHierarchy.getSiblings( getCurrentSelection() ) ); + bool bResult = !aSiblings.empty(); + if( bResult ) + { + ObjectHierarchy::tChildContainer::const_iterator aIt( + std::find( aSiblings.begin(), aSiblings.end(), getCurrentSelection())); + assert(aIt != aSiblings.end()); + if( ++aIt == aSiblings.end()) + aIt = aSiblings.begin(); + setCurrentSelection( *aIt ); + } + else + bResult = veryFirst(); + + return bResult; +} + +bool ObjectKeyNavigation::previous() +{ + ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider ); + ObjectHierarchy::tChildContainer aSiblings( aHierarchy.getSiblings( getCurrentSelection())); + bool bResult = !aSiblings.empty(); + if( bResult ) + { + ObjectHierarchy::tChildContainer::const_iterator aIt( + std::find( aSiblings.begin(), aSiblings.end(), getCurrentSelection())); + OSL_ASSERT( aIt != aSiblings.end()); + if( aIt == aSiblings.begin()) + aIt = aSiblings.end(); + --aIt; + setCurrentSelection( *aIt ); + } + else + bResult = veryLast(); + return bResult; +} + +bool ObjectKeyNavigation::up() +{ + ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider ); + bool bResult = !ObjectHierarchy::isRootNode( getCurrentSelection()); + if( bResult ) + setCurrentSelection( aHierarchy.getParent( getCurrentSelection())); + return bResult; +} + +bool ObjectKeyNavigation::down() +{ + ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider ); + bool bResult = aHierarchy.hasChildren( getCurrentSelection()); + if( bResult ) + { + ObjectHierarchy::tChildContainer aChildren = aHierarchy.getChildren( getCurrentSelection()); + OSL_ASSERT( !aChildren.empty()); + setCurrentSelection( aChildren.front()); + } + return bResult; +} + +bool ObjectKeyNavigation::veryFirst() +{ + ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider ); + ObjectHierarchy::tChildContainer aChildren( aHierarchy.getTopLevelChildren()); + bool bResult = !aChildren.empty(); + if( bResult ) + setCurrentSelection( aChildren.front()); + return bResult; +} + +bool ObjectKeyNavigation::veryLast() +{ + ObjectHierarchy aHierarchy( m_xChartDocument, m_pExplicitValueProvider ); + ObjectHierarchy::tChildContainer aChildren( aHierarchy.getTopLevelChildren()); + bool bResult = !aChildren.empty(); + if( bResult ) + setCurrentSelection( aChildren.back()); + return bResult; +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/PositionAndSizeHelper.cxx b/chart2/source/controller/main/PositionAndSizeHelper.cxx new file mode 100644 index 0000000000..50678d145b --- /dev/null +++ b/chart2/source/controller/main/PositionAndSizeHelper.cxx @@ -0,0 +1,179 @@ +/* -*- 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 <PositionAndSizeHelper.hxx> +#include <ControllerLockGuard.hxx> +#include <com/sun/star/chart/ChartLegendExpansion.hpp> +#include <com/sun/star/chart2/RelativePosition.hpp> +#include <com/sun/star/chart2/RelativeSize.hpp> +#include <tools/gen.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <ChartModel.hxx> +#include <Diagram.hxx> + +namespace chart +{ +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; + +bool PositionAndSizeHelper::moveObject( ObjectType eObjectType + , const uno::Reference< beans::XPropertySet >& xObjectProp + , const awt::Rectangle& rNewPositionAndSize + , const awt::Rectangle& rOldPositionAndSize + , const awt::Rectangle& rPageRectangle + ) +{ + if(!xObjectProp.is()) + return false; + tools::Rectangle aObjectRect( Point(rNewPositionAndSize.X,rNewPositionAndSize.Y), Size(rNewPositionAndSize.Width,rNewPositionAndSize.Height) ); + tools::Rectangle aPageRect( Point(rPageRectangle.X,rPageRectangle.Y), Size(rPageRectangle.Width,rPageRectangle.Height) ); + + // every following branch divides by width and height + if (aPageRect.getOpenWidth() == 0 || aPageRect.getOpenHeight() == 0) + return false; + + if( eObjectType==OBJECTTYPE_TITLE ) + { + //@todo decide whether x is primary or secondary + chart2::RelativePosition aRelativePosition; + aRelativePosition.Anchor = drawing::Alignment_CENTER; + //the anchor point at the title object is top/middle + Point aPos = aObjectRect.TopLeft(); + aRelativePosition.Primary = (double(aPos.X())+double(aObjectRect.getOpenWidth())/2.0)/double(aPageRect.getOpenWidth()); + aRelativePosition.Secondary = (double(aPos.Y())+double(aObjectRect.getOpenHeight())/2.0)/double(aPageRect.getOpenHeight()); + xObjectProp->setPropertyValue( "RelativePosition", uno::Any(aRelativePosition) ); + } + else if( eObjectType == OBJECTTYPE_DATA_LABEL ) + { + RelativePosition aAbsolutePosition; + RelativePosition aCustomLabelPosition; + aAbsolutePosition.Primary = double(rOldPositionAndSize.X) / double(aPageRect.getOpenWidth()); + aAbsolutePosition.Secondary = double(rOldPositionAndSize.Y) / double(aPageRect.getOpenHeight()); + + if( xObjectProp->getPropertyValue("CustomLabelPosition") >>= aCustomLabelPosition ) + { + aAbsolutePosition.Primary -= aCustomLabelPosition.Primary; + aAbsolutePosition.Secondary -= aCustomLabelPosition.Secondary; + } + + //the anchor point at the data label object is top/left + Point aPos = aObjectRect.TopLeft(); + double fRotation = 0.0; + xObjectProp->getPropertyValue("TextRotation") >>= fRotation; + if( fRotation == 90.0 ) + aPos = aObjectRect.BottomLeft(); + else if( fRotation == 270.0 ) + aPos = aObjectRect.TopRight(); + + aCustomLabelPosition.Primary = double(aPos.X()) / double(aPageRect.getOpenWidth()) - aAbsolutePosition.Primary; + aCustomLabelPosition.Secondary = double(aPos.Y()) / double(aPageRect.getOpenHeight()) - aAbsolutePosition.Secondary; + xObjectProp->setPropertyValue("CustomLabelPosition", uno::Any(aCustomLabelPosition)); + } + else if( eObjectType==OBJECTTYPE_DATA_CURVE_EQUATION ) + { + //@todo decide whether x is primary or secondary + chart2::RelativePosition aRelativePosition; + aRelativePosition.Anchor = drawing::Alignment_TOP_LEFT; + //the anchor point at the title object is top/middle + Point aPos = aObjectRect.TopLeft(); + aRelativePosition.Primary = double(aPos.X())/double(aPageRect.getOpenWidth()); + aRelativePosition.Secondary = double(aPos.Y())/double(aPageRect.getOpenHeight()); + xObjectProp->setPropertyValue( "RelativePosition", uno::Any(aRelativePosition) ); + } + else if(eObjectType==OBJECTTYPE_LEGEND) + { + xObjectProp->setPropertyValue( "Expansion", uno::Any(css::chart::ChartLegendExpansion_CUSTOM)); + chart2::RelativePosition aRelativePosition; + chart2::RelativeSize aRelativeSize; + Point aAnchor = aObjectRect.TopLeft(); + + aRelativePosition.Primary = + static_cast< double >( aAnchor.X()) / + static_cast< double >( aPageRect.getOpenWidth() ); + aRelativePosition.Secondary = + static_cast< double >( aAnchor.Y()) / + static_cast< double >( aPageRect.getOpenHeight()); + + xObjectProp->setPropertyValue( "RelativePosition", uno::Any(aRelativePosition) ); + + aRelativeSize.Primary = + static_cast< double >( aObjectRect.getOpenWidth()) / + static_cast< double >( aPageRect.getOpenWidth() ); + if (aRelativeSize.Primary > 1.0) + aRelativeSize.Primary = 1.0; + aRelativeSize.Secondary = + static_cast< double >( aObjectRect.getOpenHeight()) / + static_cast< double >( aPageRect.getOpenHeight()); + if (aRelativeSize.Secondary > 1.0) + aRelativeSize.Secondary = 1.0; + + xObjectProp->setPropertyValue( "RelativeSize", uno::Any(aRelativeSize) ); + } + else if(eObjectType==OBJECTTYPE_DIAGRAM || eObjectType==OBJECTTYPE_DIAGRAM_WALL || eObjectType==OBJECTTYPE_DIAGRAM_FLOOR) + { + //@todo decide whether x is primary or secondary + + //set position: + chart2::RelativePosition aRelativePosition; + aRelativePosition.Anchor = drawing::Alignment_CENTER; + + Point aPos = aObjectRect.Center(); + aRelativePosition.Primary = double(aPos.X())/double(aPageRect.getOpenWidth()); + aRelativePosition.Secondary = double(aPos.Y())/double(aPageRect.getOpenHeight()); + xObjectProp->setPropertyValue( "RelativePosition", uno::Any(aRelativePosition) ); + + //set size: + RelativeSize aRelativeSize; + //the anchor points for the diagram are in the middle of the diagram + //and in the middle of the page + aRelativeSize.Primary = double(aObjectRect.getOpenWidth())/double(aPageRect.getOpenWidth()); + aRelativeSize.Secondary = double(aObjectRect.getOpenHeight())/double(aPageRect.getOpenHeight()); + xObjectProp->setPropertyValue( "RelativeSize", uno::Any(aRelativeSize) ); + } + else + return false; + return true; +} + +bool PositionAndSizeHelper::moveObject( std::u16string_view rObjectCID + , const rtl::Reference<::chart::ChartModel>& xChartModel + , const awt::Rectangle& rNewPositionAndSize + , const awt::Rectangle& rOldPositionAndSize + , const awt::Rectangle& rPageRectangle + ) +{ + ControllerLockGuardUNO aLockedControllers( xChartModel ); + + awt::Rectangle aNewPositionAndSize( rNewPositionAndSize ); + + uno::Reference< beans::XPropertySet > xObjectProp = ObjectIdentifier::getObjectPropertySet( rObjectCID, xChartModel ); + ObjectType eObjectType( ObjectIdentifier::getObjectType( rObjectCID ) ); + if(eObjectType==OBJECTTYPE_DIAGRAM || eObjectType==OBJECTTYPE_DIAGRAM_WALL || eObjectType==OBJECTTYPE_DIAGRAM_FLOOR) + { + xObjectProp = ObjectIdentifier::getDiagramForCID( rObjectCID, xChartModel ); + if(!xObjectProp.is()) + return false; + } + return moveObject( eObjectType, xObjectProp, aNewPositionAndSize, rOldPositionAndSize, rPageRectangle ); +} + +} //namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/SelectionHelper.cxx b/chart2/source/controller/main/SelectionHelper.cxx new file mode 100644 index 0000000000..11fc5d9fae --- /dev/null +++ b/chart2/source/controller/main/SelectionHelper.cxx @@ -0,0 +1,651 @@ +/* -*- 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 <SelectionHelper.hxx> +#include <ObjectIdentifier.hxx> +#include <DiagramHelper.hxx> +#include <Diagram.hxx> +#include <ChartModelHelper.hxx> +#include <ChartModel.hxx> + +#include <svx/svdpage.hxx> +#include <svx/svditer.hxx> +#include <svx/obj3d.hxx> +#include <svx/svdopath.hxx> +#include <vcl/svapp.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <osl/diagnose.h> + +namespace chart +{ +using namespace ::com::sun::star; + +namespace +{ + +OUString lcl_getObjectName( SdrObject const * pObj ) +{ + if(pObj) + return pObj->GetName(); + return OUString(); +} + +void impl_selectObject( SdrObject* pObjectToSelect, DrawViewWrapper& rDrawViewWrapper ) +{ + SolarMutexGuard aSolarGuard; + + if(pObjectToSelect) + { + SelectionHelper aSelectionHelper( pObjectToSelect ); + SdrObject* pMarkObj = aSelectionHelper.getObjectToMark(); + rDrawViewWrapper.setMarkHandleProvider(&aSelectionHelper); + rDrawViewWrapper.MarkObject(pMarkObj); + rDrawViewWrapper.setMarkHandleProvider(nullptr); + } +} + +}//anonymous namespace + +bool Selection::hasSelection() const +{ + return m_aSelectedOID.isValid(); +} + +OUString const & Selection::getSelectedCID() const +{ + return m_aSelectedOID.getObjectCID(); +} + +uno::Reference< drawing::XShape > const & Selection::getSelectedAdditionalShape() const +{ + return m_aSelectedOID.getAdditionalShape(); +} + +bool Selection::setSelection( const OUString& rCID ) +{ + if ( rCID != m_aSelectedOID.getObjectCID() ) + { + m_aSelectedOID = ObjectIdentifier( rCID ); + return true; + } + return false; +} + +bool Selection::setSelection( const uno::Reference< drawing::XShape >& xShape ) +{ + if ( !( xShape == m_aSelectedOID.getAdditionalShape() ) ) + { + clearSelection(); + m_aSelectedOID = ObjectIdentifier( xShape ); + return true; + } + return false; +} + +void Selection::clearSelection() +{ + m_aSelectedOID = ObjectIdentifier(); + m_aSelectedOID_beforeMouseDown = ObjectIdentifier(); + m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = ObjectIdentifier(); +} + +bool Selection::maybeSwitchSelectionAfterSingleClickWasEnsured() +{ + if ( m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing.isValid() + && m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing != m_aSelectedOID ) + { + m_aSelectedOID = m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing; + m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = ObjectIdentifier(); + return true; + } + return false; +} + +void Selection::resetPossibleSelectionAfterSingleClickWasEnsured() +{ + if ( m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing.isValid() ) + { + m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = ObjectIdentifier(); + } +} + +void Selection::remindSelectionBeforeMouseDown() +{ + m_aSelectedOID_beforeMouseDown = m_aSelectedOID; +} + +bool Selection::isSelectionDifferentFromBeforeMouseDown() const +{ + return ( m_aSelectedOID != m_aSelectedOID_beforeMouseDown ); +} + +void Selection::applySelection( DrawViewWrapper* pDrawViewWrapper ) +{ + if( !pDrawViewWrapper ) + return; + + { + SolarMutexGuard aSolarGuard; + pDrawViewWrapper->UnmarkAll(); + } + SdrObject* pObjectToSelect = nullptr; + if ( m_aSelectedOID.isAutoGeneratedObject() ) + { + pObjectToSelect = pDrawViewWrapper->getNamedSdrObject( m_aSelectedOID.getObjectCID() ); + } + else if( m_aSelectedOID.isAdditionalShape() ) + { + pObjectToSelect = DrawViewWrapper::getSdrObject( m_aSelectedOID.getAdditionalShape() ); + } + + impl_selectObject( pObjectToSelect, *pDrawViewWrapper ); +} + +void Selection::adaptSelectionToNewPos( const Point& rMousePos, DrawViewWrapper const * pDrawViewWrapper + , bool bIsRightMouse, bool bWaitingForDoubleClick ) +{ + if( !pDrawViewWrapper ) + return; + + //do not toggle multiclick selection if right clicked on the selected object or waiting for double click + bool bAllowMultiClickSelectionChange = !bIsRightMouse && !bWaitingForDoubleClick; + + ObjectIdentifier aLastSelectedObject( m_aSelectedOID ); + + SolarMutexGuard aSolarGuard; + + //bAllowMultiClickSelectionChange==true -> a second click on the same object can lead to a changed selection (e.g. series -> single data point) + + //get object to select: + { + m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = ObjectIdentifier(); + + //the search for the object to select starts with the hit object deepest in the grouping hierarchy (a leaf in the tree) + //further we travel along the grouping hierarchy from child to parent + SdrObject* pNewObj = pDrawViewWrapper->getHitObject(rMousePos); + m_aSelectedOID = ObjectIdentifier( lcl_getObjectName( pNewObj ) );//name of pNewObj + + //ignore handle only objects for hit test + while( pNewObj && m_aSelectedOID.getObjectCID().match( "HandlesOnly" ) ) + { + pNewObj->SetMarkProtect(true); + pNewObj = pDrawViewWrapper->getHitObject(rMousePos); + m_aSelectedOID = ObjectIdentifier( lcl_getObjectName( pNewObj ) ); + } + + //accept only named objects while searching for the object to select + //this call may change m_aSelectedOID + if ( SelectionHelper::findNamedParent( pNewObj, m_aSelectedOID, true ) ) + { + //if the so far found object is a multi click object further steps are necessary + while( ObjectIdentifier::isMultiClickObject( m_aSelectedOID.getObjectCID() ) ) + { + bool bSameObjectAsLastSelected = ( aLastSelectedObject == m_aSelectedOID ); + if( bSameObjectAsLastSelected ) + { + //if the same child is clicked again don't go up further + break; + } + if ( ObjectIdentifier::areSiblings( aLastSelectedObject.getObjectCID(), m_aSelectedOID.getObjectCID() ) ) + { + //if a sibling of the last selected object is clicked don't go up further + break; + } + ObjectIdentifier aLastChild = m_aSelectedOID; + if ( !SelectionHelper::findNamedParent( pNewObj, m_aSelectedOID, false ) ) + { + //take the one found so far + break; + } + //if the last selected object is found don't go up further + //but take the last child if selection change is allowed + if ( aLastSelectedObject == m_aSelectedOID ) + { + if( bAllowMultiClickSelectionChange ) + { + m_aSelectedOID = aLastChild; + } + else + m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = aLastChild; + break; + } + } + + OSL_ENSURE(m_aSelectedOID.isValid(), "somehow lost selected object"); + } + else + { + //maybe an additional shape was hit + if ( pNewObj ) + { + m_aSelectedOID = ObjectIdentifier( uno::Reference< drawing::XShape >( pNewObj->getUnoShape(), uno::UNO_QUERY ) ); + } + else + { + m_aSelectedOID = ObjectIdentifier(); + } + } + + if ( !m_aSelectedOID.isAdditionalShape() ) + { + OUString aPageCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, u"" ) );//@todo read CID from model + + if ( !m_aSelectedOID.isAutoGeneratedObject() ) + { + m_aSelectedOID = ObjectIdentifier( aPageCID ); + } + + //check whether the diagram was hit but not selected (e.g. because it has no filling): + OUString aDiagramCID = ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM, OUString::number( 0 ) ); + OUString aWallCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL, u"" ) );//@todo read CID from model + bool bBackGroundHit = m_aSelectedOID.getObjectCID() == aPageCID || m_aSelectedOID.getObjectCID() == aWallCID || !m_aSelectedOID.isAutoGeneratedObject(); + if( bBackGroundHit ) + { + //todo: if more than one diagram is available in future do check the list of all diagrams here + SdrObject* pDiagram = pDrawViewWrapper->getNamedSdrObject( aDiagramCID ); + if( pDiagram ) + { + if( DrawViewWrapper::IsObjectHit( pDiagram, rMousePos ) ) + { + m_aSelectedOID = ObjectIdentifier( aDiagramCID ); + } + } + } + //check whether the legend was hit but not selected (e.g. because it has no filling): + if( bBackGroundHit || m_aSelectedOID.getObjectCID() == aDiagramCID ) + { + OUString aLegendCID( ObjectIdentifier::createClassifiedIdentifierForParticle( ObjectIdentifier::createParticleForLegend(nullptr) ) );//@todo read CID from model + SdrObject* pLegend = pDrawViewWrapper->getNamedSdrObject( aLegendCID ); + if( pLegend ) + { + if( DrawViewWrapper::IsObjectHit( pLegend, rMousePos ) ) + { + m_aSelectedOID = ObjectIdentifier( aLegendCID ); + } + } + } + } + } + + if ( bIsRightMouse && m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing.isValid() ) + { + m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = ObjectIdentifier(); + } +} + +bool Selection::isResizeableObjectSelected() const +{ + ObjectType eObjectType = m_aSelectedOID.getObjectType(); + switch( eObjectType ) + { + case OBJECTTYPE_DIAGRAM: + case OBJECTTYPE_DIAGRAM_WALL: + case OBJECTTYPE_SHAPE: + case OBJECTTYPE_LEGEND: + return true; + default: + return false; + } +} + +bool Selection::isRotateableObjectSelected( const rtl::Reference<::chart::ChartModel>& xChartModel ) const +{ + return SelectionHelper::isRotateableObject( m_aSelectedOID.getObjectCID(), xChartModel ); +} + +bool Selection::isDragableObjectSelected() const +{ + return m_aSelectedOID.isDragableObject(); +} + +bool Selection::isAdditionalShapeSelected() const +{ + return m_aSelectedOID.isAdditionalShape(); +} + +bool SelectionHelper::findNamedParent( SdrObject*& pInOutObject + , OUString& rOutName + , bool bGivenObjectMayBeResult ) +{ + SolarMutexGuard aSolarGuard; + //find the deepest named group + SdrObject* pObj = pInOutObject; + OUString aName; + if( bGivenObjectMayBeResult ) + aName = lcl_getObjectName( pObj ); + + while( pObj && !ObjectIdentifier::isCID( aName ) ) + { + SdrObjList* pObjList = pObj->getParentSdrObjListFromSdrObject(); + if( !pObjList ) + return false; + SdrObject* pOwner = pObjList->getSdrObjectFromSdrObjList(); + if( !pOwner ) + return false; + pObj = pOwner; + aName = lcl_getObjectName( pObj ); + } + + if(!pObj) + return false; + if(aName.isEmpty()) + return false; + + pInOutObject = pObj; + rOutName = aName; + return true; +} + +bool SelectionHelper::findNamedParent( SdrObject*& pInOutObject + , ObjectIdentifier& rOutObject + , bool bGivenObjectMayBeResult ) +{ + OUString aName; + if ( findNamedParent( pInOutObject, aName, bGivenObjectMayBeResult ) ) + { + rOutObject = ObjectIdentifier( aName ); + return true; + } + return false; +} + +bool SelectionHelper::isDragableObjectHitTwice( const Point& rMPos + , const OUString& rNameOfSelectedObject + , const DrawViewWrapper& rDrawViewWrapper ) +{ + if(rNameOfSelectedObject.isEmpty()) + return false; + if( !ObjectIdentifier::isDragableObject(rNameOfSelectedObject) ) + return false; + SolarMutexGuard aSolarGuard; + SdrObject* pObj = rDrawViewWrapper.getNamedSdrObject( rNameOfSelectedObject ); + return DrawViewWrapper::IsObjectHit( pObj, rMPos ); +} + +OUString SelectionHelper::getHitObjectCID( + const Point& rMPos, + DrawViewWrapper const & rDrawViewWrapper, + bool bGetDiagramInsteadOf_Wall ) +{ + SolarMutexGuard aSolarGuard; + OUString aRet; + + SdrObject* pNewObj = rDrawViewWrapper.getHitObject(rMPos); + aRet = lcl_getObjectName( pNewObj );//name of pNewObj + + //ignore handle only objects for hit test + while( pNewObj && aRet.match("HandlesOnly") ) + { + pNewObj->SetMarkProtect(true); + pNewObj = rDrawViewWrapper.getHitObject(rMPos); + aRet = lcl_getObjectName( pNewObj ); + } + + //accept only named objects while searching for the object to select + if( !findNamedParent( pNewObj, aRet, true ) ) + { + aRet.clear(); + } + + OUString aPageCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, u"" ) );//@todo read CID from model + //get page when nothing was hit + if( aRet.isEmpty() && !pNewObj ) + { + aRet = aPageCID; + } + + //get diagram instead wall or page if hit inside diagram + if( !aRet.isEmpty() ) + { + if( aRet == aPageCID ) + { + OUString aDiagramCID = ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM, OUString::number( 0 ) ); + //todo: if more than one diagram is available in future do check the list of all diagrams here + SdrObject* pDiagram = rDrawViewWrapper.getNamedSdrObject( aDiagramCID ); + if( pDiagram ) + { + if( DrawViewWrapper::IsObjectHit( pDiagram, rMPos ) ) + { + aRet = aDiagramCID; + } + } + } + else if( bGetDiagramInsteadOf_Wall ) + { + OUString aWallCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL, u"" ) );//@todo read CID from model + + if( aRet == aWallCID ) + { + OUString aDiagramCID = ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM, OUString::number( 0 ) ); + aRet = aDiagramCID; + } + } + } + + return aRet; + // \\- solar mutex +} + +bool SelectionHelper::isRotateableObject( std::u16string_view rCID + , const rtl::Reference<::chart::ChartModel>& xChartModel ) +{ + if( !ObjectIdentifier::isRotateableObject( rCID ) ) + return false; + + sal_Int32 nDimensionCount = xChartModel->getFirstChartDiagram()->getDimension(); + + return nDimensionCount == 3; +} + +SelectionHelper::SelectionHelper( SdrObject* pSelectedObj ) + : m_pSelectedObj( pSelectedObj ), m_pMarkObj(nullptr) +{ + +} +SelectionHelper::~SelectionHelper() +{ +} + +bool SelectionHelper::getFrameDragSingles() +{ + //true == green == surrounding handles + return DynCastE3dObject( m_pSelectedObj) == nullptr; +} + +SdrObject* SelectionHelper::getMarkHandlesObject( SdrObject* pObj ) +{ + if(!pObj) + return nullptr; + OUString aName( lcl_getObjectName( pObj ) ); + if( aName.match("MarkHandles") || aName.match("HandlesOnly") ) + return pObj; + if( !aName.isEmpty() )//don't get the markhandles of a different object + return nullptr; + + //search for a child with name "MarkHandles" or "HandlesOnly" + SolarMutexGuard aSolarGuard; + SdrObjList* pSubList = pObj->GetSubList(); + if(pSubList) + { + SdrObjListIter aIterator(pSubList, SdrIterMode::Flat); + while (aIterator.IsMore()) + { + SdrObject* pMarkHandles = SelectionHelper::getMarkHandlesObject( aIterator.Next() ); + if( pMarkHandles ) + return pMarkHandles; + } + } + return nullptr; +} + +SdrObject* SelectionHelper::getObjectToMark() +{ + //return the selected object itself + //or a specific other object if that exists + SdrObject* pObj = m_pSelectedObj; + m_pMarkObj = pObj; + + //search for a child with name "MarkHandles" or "HandlesOnly" + if(pObj) + { + SolarMutexGuard aSolarGuard; + SdrObjList* pSubList = pObj->GetSubList(); + if(pSubList) + { + SdrObjListIter aIterator(pSubList, SdrIterMode::Flat); + while (aIterator.IsMore()) + { + SdrObject* pMarkHandles = SelectionHelper::getMarkHandlesObject( aIterator.Next() ); + if( pMarkHandles ) + { + m_pMarkObj = pMarkHandles; + break; + } + } + } + } + return m_pMarkObj; +} + +E3dScene* SelectionHelper::getSceneToRotate( SdrObject* pObj ) +{ + //search whether the object or one of its children is a 3D object + //if so, return the accessory 3DScene + + E3dObject* pRotateable = nullptr; + + if(pObj) + { + pRotateable = DynCastE3dObject(pObj); + if( !pRotateable ) + { + SolarMutexGuard aSolarGuard; + SdrObjList* pSubList = pObj->GetSubList(); + if(pSubList) + { + SdrObjListIter aIterator(pSubList, SdrIterMode::DeepWithGroups); + while( aIterator.IsMore() && !pRotateable ) + { + pRotateable = DynCastE3dObject(aIterator.Next()); + } + } + } + } + + E3dScene* pScene(nullptr); + + if(pRotateable) + { + SolarMutexGuard aSolarGuard; + pScene = pRotateable->getRootE3dSceneFromE3dObject(); + } + + return pScene; +} + +bool SelectionHelper::getMarkHandles( SdrHdlList& rHdlList ) +{ + SolarMutexGuard aSolarGuard; + + //@todo -> more flexible handle creation + //2 scenarios possible: + //1. add an additional invisible shape as a child to the selected object + //this child needs to be named somehow and handles need to be generated there from... + //or 2. offer a central service per view where renderer and so can register for handle creation for a special shape + //.. or 3. feature from drawinglayer to create handles for each shape... (bad performance... ?) ? + + //scenario 1 is now used: + //if a child with name MarkHandles exists + //this child is marked instead of the logical selected object + +/* + //if a special mark object was found + //that object should be used for marking only + if( m_pMarkObj != m_pSelectedObj) + return false; +*/ + //if a special mark object was found + //that object should be used to create handles from + if( m_pMarkObj && m_pMarkObj != m_pSelectedObj) + { + rHdlList.Clear(); + if( auto pPathObj = dynamic_cast<const SdrPathObj*>( m_pMarkObj) ) + { + //if th object is a polygon + //from each point a handle is generated + const ::basegfx::B2DPolyPolygon& rPolyPolygon = pPathObj->GetPathPoly(); + for( sal_uInt32 nN = 0; nN < rPolyPolygon.count(); nN++) + { + const ::basegfx::B2DPolygon& aPolygon(rPolyPolygon.getB2DPolygon(nN)); + for( sal_uInt32 nM = 0; nM < aPolygon.count(); nM++) + { + const ::basegfx::B2DPoint aPoint(aPolygon.getB2DPoint(nM)); + rHdlList.AddHdl(std::make_unique<SdrHdl>(Point(basegfx::fround(aPoint.getX()), basegfx::fround(aPoint.getY())), SdrHdlKind::Poly)); + } + } + return true; + } + else + return false; //use the special MarkObject for marking + } + + //@todo: + //add and document good marking defaults ... + + rHdlList.Clear(); + + SdrObject* pObj = m_pSelectedObj; + if(!pObj) + return false; + SdrObjList* pSubList = pObj->GetSubList(); + if( !pSubList )//no group object !pObj->IsGroupObject() + return false; + + OUString aName( lcl_getObjectName( pObj ) ); + ObjectType eObjectType( ObjectIdentifier::getObjectType( aName ) ); + if( eObjectType == OBJECTTYPE_DATA_POINT + || eObjectType == OBJECTTYPE_DATA_LABEL + || eObjectType == OBJECTTYPE_LEGEND_ENTRY + || eObjectType == OBJECTTYPE_AXIS_UNITLABEL ) + { + return false; + } + + SdrObjListIter aIterator(pSubList, SdrIterMode::Flat); + + while (aIterator.IsMore()) + { + SdrObject* pSubObj = aIterator.Next(); + if( eObjectType == OBJECTTYPE_DATA_SERIES ) + { + OUString aSubName( lcl_getObjectName( pSubObj ) ); + ObjectType eSubObjectType( ObjectIdentifier::getObjectType( aSubName ) ); + if( eSubObjectType!=OBJECTTYPE_DATA_POINT ) + return false; + } + + Point aPos = pSubObj->GetCurrentBoundRect().Center(); + rHdlList.AddHdl(std::make_unique<SdrHdl>(aPos,SdrHdlKind::Poly)); + } + return true; +} + +} //namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ShapeController.cxx b/chart2/source/controller/main/ShapeController.cxx new file mode 100644 index 0000000000..97715b07c2 --- /dev/null +++ b/chart2/source/controller/main/ShapeController.cxx @@ -0,0 +1,673 @@ +/* -*- 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 "ShapeController.hxx" +#include <ChartController.hxx> +#include <ViewElementListProvider.hxx> +#include <dlg_ShapeFont.hxx> +#include <dlg_ShapeParagraph.hxx> +#include <ChartModel.hxx> +#include <chartview/DrawModelWrapper.hxx> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/frame/CommandGroup.hpp> + +#include <vcl/svapp.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/editids.hrc> +#include <editeng/eeitem.hxx> +#include <editeng/hyphenzoneitem.hxx> +#include <editeng/orphitem.hxx> +#include <editeng/spltitem.hxx> +#include <svx/svxdlg.hxx> +#include <editeng/widwitem.hxx> +#include <comphelper/diagnose_ex.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::frame; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace chart +{ + +ShapeController::ShapeController( const Reference< uno::XComponentContext >& rxContext, + ChartController* pController ) + :FeatureCommandDispatchBase( rxContext ) + ,m_pChartController( pController ) +{ +} + +ShapeController::~ShapeController() +{ +} + +// WeakComponentImplHelperBase +void ShapeController::disposing(std::unique_lock<std::mutex>& /*rGuard*/) +{ +} + +// XEventListener +void ShapeController::disposing( const lang::EventObject& /* Source */ ) +{ +} + +FeatureState ShapeController::getState( const OUString& rCommand ) +{ + FeatureState aReturn; + aReturn.bEnabled = false; + aReturn.aState <<= false; + + bool bWritable = false; + if ( m_pChartController ) + { + rtl::Reference< ChartModel > xStorable = m_pChartController->getChartModel(); + if ( xStorable.is() ) + { + bWritable = !xStorable->isReadonly(); + } + } + + SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.find( rCommand ); + if ( aIter != m_aSupportedFeatures.end() ) + { + ChartCommandID nFeatureId = aIter->second.nFeatureId; + switch ( nFeatureId ) + { + case ChartCommandID::ShapeFormatLine: + case ChartCommandID::ShapeFormatArea: + case ChartCommandID::ShapeTextAttributes: + case ChartCommandID::ShapeTransformDialog: + case ChartCommandID::ShapeObjectTitleDescription: + case ChartCommandID::ShapeRenameObject: + { + aReturn.bEnabled = bWritable; + aReturn.aState <<= false; + } + break; + case ChartCommandID::ShapeBringToFront: + case ChartCommandID::ShapeForward: + { + aReturn.bEnabled = ( bWritable && isForwardPossible() ); + aReturn.aState <<= false; + } + break; + case ChartCommandID::ShapeBackward: + case ChartCommandID::ShapeSendToBack: + { + + aReturn.bEnabled = ( bWritable && isBackwardPossible() ); + aReturn.aState <<= false; + } + break; + case ChartCommandID::ShapeFontDialog: + case ChartCommandID::ShapeParagraphDialog: + { + aReturn.bEnabled = bWritable; + aReturn.aState <<= false; + } + break; + default: + { + aReturn.bEnabled = false; + aReturn.aState <<= false; + } + break; + } + } + + return aReturn; +} + +void ShapeController::execute( const OUString& rCommand, const Sequence< beans::PropertyValue>& ) +{ + SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.find( rCommand ); + if ( aIter == m_aSupportedFeatures.end() ) + return; + + ChartCommandID nFeatureId = aIter->second.nFeatureId; + switch ( nFeatureId ) + { + case ChartCommandID::ShapeFormatLine: + { + executeDispatch_FormatLine(); + } + break; + case ChartCommandID::ShapeFormatArea: + { + executeDispatch_FormatArea(); + } + break; + case ChartCommandID::ShapeTextAttributes: + { + executeDispatch_TextAttributes(); + } + break; + case ChartCommandID::ShapeTransformDialog: + { + executeDispatch_TransformDialog(); + } + break; + case ChartCommandID::ShapeObjectTitleDescription: + { + executeDispatch_ObjectTitleDescription(); + } + break; + case ChartCommandID::ShapeRenameObject: + { + executeDispatch_RenameObject(); + } + break; + case ChartCommandID::ShapeBringToFront: + case ChartCommandID::ShapeForward: + case ChartCommandID::ShapeBackward: + case ChartCommandID::ShapeSendToBack: + { + executeDispatch_ChangeZOrder( nFeatureId ); + } + break; + case ChartCommandID::ShapeFontDialog: + { + executeDispatch_FontDialog(); + } + break; + case ChartCommandID::ShapeParagraphDialog: + { + executeDispatch_ParagraphDialog(); + } + break; + default: + { + } + break; + } +} + +void ShapeController::describeSupportedFeatures() +{ + implDescribeSupportedFeature( ".uno:FormatLine", ChartCommandID::ShapeFormatLine, CommandGroup::FORMAT ); + implDescribeSupportedFeature( ".uno:FormatArea", ChartCommandID::ShapeFormatArea, CommandGroup::FORMAT ); + implDescribeSupportedFeature( ".uno:TextAttributes", ChartCommandID::ShapeTextAttributes, CommandGroup::FORMAT ); + implDescribeSupportedFeature( ".uno:TransformDialog", ChartCommandID::ShapeTransformDialog, CommandGroup::FORMAT ); + implDescribeSupportedFeature( ".uno:ObjectTitleDescription", ChartCommandID::ShapeObjectTitleDescription, CommandGroup::FORMAT ); + implDescribeSupportedFeature( ".uno:RenameObject", ChartCommandID::ShapeRenameObject, CommandGroup::FORMAT ); + implDescribeSupportedFeature( ".uno:BringToFront", ChartCommandID::ShapeBringToFront, CommandGroup::FORMAT ); + implDescribeSupportedFeature( ".uno:Forward", ChartCommandID::ShapeForward, CommandGroup::FORMAT ); + implDescribeSupportedFeature( ".uno:Backward", ChartCommandID::ShapeBackward, CommandGroup::FORMAT ); + implDescribeSupportedFeature( ".uno:SendToBack", ChartCommandID::ShapeSendToBack, CommandGroup::FORMAT ); + implDescribeSupportedFeature( ".uno:FontDialog", ChartCommandID::ShapeFontDialog, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:ParagraphDialog", ChartCommandID::ShapeParagraphDialog, CommandGroup::EDIT ); +} + +IMPL_LINK( ShapeController, CheckNameHdl, AbstractSvxObjectNameDialog&, rDialog, bool ) +{ + OUString aName; + rDialog.GetName( aName ); + + if ( !aName.isEmpty() ) + { + DrawViewWrapper* pDrawViewWrapper = ( m_pChartController ? m_pChartController->GetDrawViewWrapper() : nullptr ); + if ( pDrawViewWrapper && pDrawViewWrapper->getNamedSdrObject( aName ) ) + { + return false; + } + } + return true; +} + +void ShapeController::executeDispatch_FormatLine() +{ + SolarMutexGuard aGuard; + if ( !m_pChartController ) + return; + + weld::Window* pChartWindow(m_pChartController->GetChartFrame()); + DrawModelWrapper* pDrawModelWrapper = m_pChartController->GetDrawModelWrapper(); + DrawViewWrapper* pDrawViewWrapper = m_pChartController->GetDrawViewWrapper(); + if ( !(pChartWindow && pDrawModelWrapper && pDrawViewWrapper) ) + return; + + SdrObject* pSelectedObj = pDrawViewWrapper->getSelectedObject(); + SfxItemSet aAttr( pDrawViewWrapper->GetDefaultAttr() ); + bool bHasMarked = pDrawViewWrapper->AreObjectsMarked(); + if ( bHasMarked ) + { + pDrawViewWrapper->MergeAttrFromMarked( aAttr, false ); + } + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr< SfxAbstractTabDialog > pDlg( + pFact->CreateSvxLineTabDialog(pChartWindow, &aAttr, &pDrawModelWrapper->getSdrModel(), + pSelectedObj, bHasMarked)); + if ( pDlg->Execute() == RET_OK ) + { + const SfxItemSet* pOutAttr = pDlg->GetOutputItemSet(); + if ( bHasMarked ) + { + pDrawViewWrapper->SetAttrToMarked( *pOutAttr, false ); + } + else + { + pDrawViewWrapper->SetDefaultAttr( *pOutAttr, false ); + } + } +} + +void ShapeController::executeDispatch_FormatArea() +{ + SolarMutexGuard aGuard; + if ( !m_pChartController ) + return; + + weld::Window* pChartWindow(m_pChartController->GetChartFrame()); + DrawModelWrapper* pDrawModelWrapper = m_pChartController->GetDrawModelWrapper(); + DrawViewWrapper* pDrawViewWrapper = m_pChartController->GetDrawViewWrapper(); + if ( !(pChartWindow && pDrawModelWrapper && pDrawViewWrapper) ) + return; + + SfxItemSet aAttr( pDrawViewWrapper->GetDefaultAttr() ); + bool bHasMarked = pDrawViewWrapper->AreObjectsMarked(); + if ( bHasMarked ) + { + pDrawViewWrapper->MergeAttrFromMarked( aAttr, false ); + } + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr< AbstractSvxAreaTabDialog > pDlg( + pFact->CreateSvxAreaTabDialog(pChartWindow, &aAttr, &pDrawModelWrapper->getSdrModel(), true, false)); + if ( pDlg->Execute() == RET_OK ) + { + const SfxItemSet* pOutAttr = pDlg->GetOutputItemSet(); + if ( bHasMarked ) + { + pDrawViewWrapper->SetAttrToMarked( *pOutAttr, false ); + } + else + { + pDrawViewWrapper->SetDefaultAttr( *pOutAttr, false ); + } + } +} + +void ShapeController::executeDispatch_TextAttributes() +{ + SolarMutexGuard aGuard; + if ( !m_pChartController ) + return; + + weld::Window* pChartWindow(m_pChartController->GetChartFrame()); + DrawViewWrapper* pDrawViewWrapper = m_pChartController->GetDrawViewWrapper(); + if ( !(pChartWindow && pDrawViewWrapper) ) + return; + + SfxItemSet aAttr( pDrawViewWrapper->GetDefaultAttr() ); + bool bHasMarked = pDrawViewWrapper->AreObjectsMarked(); + if ( bHasMarked ) + { + pDrawViewWrapper->MergeAttrFromMarked( aAttr, false ); + } + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr< SfxAbstractTabDialog > pDlg( + pFact->CreateTextTabDialog(pChartWindow, &aAttr, pDrawViewWrapper)); + if ( pDlg->Execute() == RET_OK ) + { + const SfxItemSet* pOutAttr = pDlg->GetOutputItemSet(); + if ( bHasMarked ) + { + pDrawViewWrapper->SetAttributes( *pOutAttr ); + } + else + { + pDrawViewWrapper->SetDefaultAttr( *pOutAttr, false ); + } + } +} + +void ShapeController::executeDispatch_TransformDialog() +{ + SolarMutexGuard aGuard; + if ( !m_pChartController ) + return; + + weld::Window* pChartWindow(m_pChartController->GetChartFrame()); + DrawViewWrapper* pDrawViewWrapper = m_pChartController->GetDrawViewWrapper(); + if ( !(pChartWindow && pDrawViewWrapper) ) + return; + + SdrObject* pSelectedObj = pDrawViewWrapper->getSelectedObject(); + if ( pSelectedObj && pSelectedObj->GetObjIdentifier() == SdrObjKind::Caption ) + { + // item set for caption + SfxItemSet aAttr(pDrawViewWrapper->GetModel().GetItemPool()); + pDrawViewWrapper->GetAttributes( aAttr ); + // item set for position and size + SfxItemSet aGeoAttr( pDrawViewWrapper->GetGeoAttrFromMarked() ); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr< SfxAbstractTabDialog > pDlg( + pFact->CreateCaptionDialog(pChartWindow, pDrawViewWrapper)); + const WhichRangesContainer& pRange = pDlg->GetInputRanges( *aAttr.GetPool() ); + SfxItemSet aCombAttr( *aAttr.GetPool(), pRange ); + aCombAttr.Put( aAttr ); + aCombAttr.Put( aGeoAttr ); + pDlg->SetInputSet( &aCombAttr ); + if ( pDlg->Execute() == RET_OK ) + { + const SfxItemSet* pOutAttr = pDlg->GetOutputItemSet(); + pDrawViewWrapper->SetAttributes( *pOutAttr ); + pDrawViewWrapper->SetGeoAttrToMarked( *pOutAttr ); + } + } + else + { + SfxItemSet aGeoAttr( pDrawViewWrapper->GetGeoAttrFromMarked() ); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr< SfxAbstractTabDialog > pDlg( + pFact->CreateSvxTransformTabDialog(pChartWindow, &aGeoAttr, pDrawViewWrapper)); + if ( pDlg->Execute() == RET_OK ) + { + const SfxItemSet* pOutAttr = pDlg->GetOutputItemSet(); + pDrawViewWrapper->SetGeoAttrToMarked( *pOutAttr ); + } + } +} + +void ShapeController::executeDispatch_ObjectTitleDescription() +{ + SolarMutexGuard aGuard; + if ( !m_pChartController ) + return; + + DrawViewWrapper* pDrawViewWrapper = m_pChartController->GetDrawViewWrapper(); + if ( !(pDrawViewWrapper && pDrawViewWrapper->GetMarkedObjectCount() == 1) ) + return; + + SdrObject* pSelectedObj = pDrawViewWrapper->getSelectedObject(); + if ( !pSelectedObj ) + return; + + OUString aTitle( pSelectedObj->GetTitle() ); + OUString aDescription( pSelectedObj->GetDescription() ); + bool isDecorative(pSelectedObj->IsDecorative()); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + weld::Window* pChartWindow(m_pChartController->GetChartFrame()); + ScopedVclPtr< AbstractSvxObjectTitleDescDialog > pDlg( + pFact->CreateSvxObjectTitleDescDialog(pChartWindow, aTitle, aDescription, isDecorative)); + if ( pDlg->Execute() == RET_OK ) + { + pDlg->GetTitle( aTitle ); + pDlg->GetDescription( aDescription ); + pDlg->IsDecorative(isDecorative); + pSelectedObj->SetTitle( aTitle ); + pSelectedObj->SetDescription( aDescription ); + pSelectedObj->SetDecorative(isDecorative); + } +} + +void ShapeController::executeDispatch_RenameObject() +{ + SolarMutexGuard aGuard; + if ( !m_pChartController ) + return; + + DrawViewWrapper* pDrawViewWrapper = m_pChartController->GetDrawViewWrapper(); + if ( !(pDrawViewWrapper && pDrawViewWrapper->GetMarkedObjectCount() == 1) ) + return; + + SdrObject* pSelectedObj = pDrawViewWrapper->getSelectedObject(); + if ( !pSelectedObj ) + return; + + OUString aName = pSelectedObj->GetName(); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + weld::Window* pChartWindow(m_pChartController->GetChartFrame()); + ScopedVclPtr< AbstractSvxObjectNameDialog > pDlg( + pFact->CreateSvxObjectNameDialog(pChartWindow, aName)); + pDlg->SetCheckNameHdl( LINK( this, ShapeController, CheckNameHdl ) ); + if ( pDlg->Execute() == RET_OK ) + { + pDlg->GetName(aName); + if (pSelectedObj->GetName() == aName) + { + pSelectedObj->SetName( aName ); + } + } +} + +void ShapeController::executeDispatch_ChangeZOrder( ChartCommandID nId ) +{ + SolarMutexGuard aGuard; + DrawViewWrapper* pDrawViewWrapper = ( m_pChartController ? m_pChartController->GetDrawViewWrapper() : nullptr ); + if ( !pDrawViewWrapper ) + return; + + switch ( nId ) + { + case ChartCommandID::ShapeBringToFront: + { + if ( isForwardPossible() ) + { + pDrawViewWrapper->PutMarkedToTop(); + } + } + break; + case ChartCommandID::ShapeForward: + { + if ( isForwardPossible() ) + { + pDrawViewWrapper->MovMarkedToTop(); + } + } + break; + case ChartCommandID::ShapeBackward: + { + if ( isBackwardPossible() ) + { + pDrawViewWrapper->MovMarkedToBtm(); + } + } + break; + case ChartCommandID::ShapeSendToBack: + { + if ( isBackwardPossible() ) + { + SdrObject* pFirstObj = getFirstAdditionalShape(); + pDrawViewWrapper->PutMarkedBehindObj( pFirstObj ); + } + } + break; + default: + { + } + break; + } +} + +void ShapeController::executeDispatch_FontDialog() +{ + SolarMutexGuard aGuard; + if ( !m_pChartController ) + return; + + weld::Window* pChartWindow(m_pChartController->GetChartFrame()); + DrawModelWrapper* pDrawModelWrapper = m_pChartController->GetDrawModelWrapper(); + DrawViewWrapper* pDrawViewWrapper = m_pChartController->GetDrawViewWrapper(); + if ( pChartWindow && pDrawModelWrapper && pDrawViewWrapper ) + { + SfxItemSet aAttr(pDrawViewWrapper->GetModel().GetItemPool()); + pDrawViewWrapper->GetAttributes( aAttr ); + ViewElementListProvider aViewElementListProvider( pDrawModelWrapper ); + ShapeFontDialog aDlg(pChartWindow, &aAttr, &aViewElementListProvider); + if (aDlg.run() == RET_OK) + { + const SfxItemSet* pOutAttr = aDlg.GetOutputItemSet(); + pDrawViewWrapper->SetAttributes( *pOutAttr ); + } + } +} + +void ShapeController::executeDispatch_ParagraphDialog() +{ + SolarMutexGuard aGuard; + if ( !m_pChartController ) + return; + + weld::Window* pChartWindow(m_pChartController->GetChartFrame()); + DrawViewWrapper* pDrawViewWrapper = m_pChartController->GetDrawViewWrapper(); + if ( !(pChartWindow && pDrawViewWrapper) ) + return; + + SfxItemPool& rPool = pDrawViewWrapper->GetModel().GetItemPool(); + SfxItemSet aAttr( rPool ); + pDrawViewWrapper->GetAttributes( aAttr ); + + SfxItemSetFixed< + EE_ITEMS_START, EE_ITEMS_END, + SID_ATTR_PARA_PAGEBREAK, SID_ATTR_PARA_WIDOWS> aNewAttr(rPool); + aNewAttr.Put( aAttr ); + aNewAttr.Put( SvxHyphenZoneItem( false, SID_ATTR_PARA_HYPHENZONE ) ); + aNewAttr.Put( SvxFormatBreakItem( SvxBreak::NONE, SID_ATTR_PARA_PAGEBREAK ) ); + aNewAttr.Put( SvxFormatSplitItem( true, SID_ATTR_PARA_SPLIT) ); + aNewAttr.Put( SvxWidowsItem( 0, SID_ATTR_PARA_WIDOWS) ); + aNewAttr.Put( SvxOrphansItem( 0, SID_ATTR_PARA_ORPHANS) ); + + ShapeParagraphDialog aDlg(pChartWindow, &aNewAttr); + if (aDlg.run() == RET_OK) + { + const SfxItemSet* pOutAttr = aDlg.GetOutputItemSet(); + pDrawViewWrapper->SetAttributes( *pOutAttr ); + } +} + +SdrObject* ShapeController::getFirstAdditionalShape() +{ + SdrObject* pFirstObj = nullptr; + + try + { + DrawModelWrapper* pDrawModelWrapper = ( m_pChartController ? m_pChartController->GetDrawModelWrapper() : nullptr ); + if ( pDrawModelWrapper ) + { + Reference< drawing::XShape > xFirstShape; + rtl::Reference<SvxDrawPage> xDrawPage( pDrawModelWrapper->getMainDrawPage() ); + Reference< drawing::XShapes > xChartRoot( DrawModelWrapper::getChartRootShape( xDrawPage ) ); + sal_Int32 nCount = xDrawPage->getCount(); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + Reference< drawing::XShape > xShape; + if ( xDrawPage->getByIndex( i ) >>= xShape ) + { + if ( xShape.is() && xShape != xChartRoot ) + { + xFirstShape = xShape; + break; + } + } + } + if ( xFirstShape.is() ) + { + pFirstObj = DrawViewWrapper::getSdrObject( xFirstShape ); + } + } + } + catch ( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + + return pFirstObj; +} + +SdrObject* ShapeController::getLastAdditionalShape() +{ + SdrObject* pLastObj = nullptr; + + try + { + DrawModelWrapper* pDrawModelWrapper = ( m_pChartController ? m_pChartController->GetDrawModelWrapper() : nullptr ); + if ( pDrawModelWrapper ) + { + Reference< drawing::XShape > xLastShape; + rtl::Reference<SvxDrawPage> xDrawPage( pDrawModelWrapper->getMainDrawPage() ); + Reference< drawing::XShapes > xChartRoot( DrawModelWrapper::getChartRootShape( xDrawPage ) ); + sal_Int32 nCount = xDrawPage->getCount(); + for ( sal_Int32 i = nCount - 1; i >= 0; --i ) + { + Reference< drawing::XShape > xShape; + if ( xDrawPage->getByIndex( i ) >>= xShape ) + { + if ( xShape.is() && xShape != xChartRoot ) + { + xLastShape = xShape; + break; + } + } + } + if ( xLastShape.is() ) + { + pLastObj = DrawViewWrapper::getSdrObject( xLastShape ); + } + } + } + catch ( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + + return pLastObj; +} + +bool ShapeController::isBackwardPossible() +{ + if ( m_pChartController && m_pChartController->isAdditionalShapeSelected() ) + { + SolarMutexGuard aGuard; + DrawViewWrapper* pDrawViewWrapper = m_pChartController->GetDrawViewWrapper(); + if ( pDrawViewWrapper ) + { + SdrObject* pSelectedObj = pDrawViewWrapper->getSelectedObject(); + SdrObject* pFirstObj = getFirstAdditionalShape(); + if ( pSelectedObj && pFirstObj && pSelectedObj != pFirstObj ) + { + return true; + } + } + } + return false; +} + +bool ShapeController::isForwardPossible() +{ + if ( m_pChartController && m_pChartController->isAdditionalShapeSelected() ) + { + SolarMutexGuard aGuard; + DrawViewWrapper* pDrawViewWrapper = m_pChartController->GetDrawViewWrapper(); + if ( pDrawViewWrapper ) + { + SdrObject* pSelectedObj = pDrawViewWrapper->getSelectedObject(); + SdrObject* pLastObj = getLastAdditionalShape(); + if ( pSelectedObj && pLastObj && pSelectedObj != pLastObj ) + { + return true; + } + } + } + return false; +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ShapeController.hxx b/chart2/source/controller/main/ShapeController.hxx new file mode 100644 index 0000000000..cdd8002ce2 --- /dev/null +++ b/chart2/source/controller/main/ShapeController.hxx @@ -0,0 +1,81 @@ +/* -*- 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 . + */ +#pragma once + +#include "FeatureCommandDispatchBase.hxx" +#include <tools/link.hxx> + +class AbstractSvxObjectNameDialog; +class SdrObject; + +namespace chart +{ + +class ChartController; + +/** This is a CommandDispatch implementation for shapes. + */ +class ShapeController: public FeatureCommandDispatchBase +{ + friend class ControllerCommandDispatch; + +public: + ShapeController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, ChartController* pController ); + virtual ~ShapeController() override; + +protected: + // WeakComponentImplHelperBase + virtual void disposing(std::unique_lock<std::mutex>& rGuard) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // state of a feature + virtual FeatureState getState( const OUString& rCommand ) override; + + // execute a feature + virtual void execute( const OUString& rCommand, const css::uno::Sequence< css::beans::PropertyValue>& rArgs ) override; + + // all the features which should be handled by this class + virtual void describeSupportedFeatures() override; + +private: + DECL_LINK( CheckNameHdl, AbstractSvxObjectNameDialog&, bool); + + void executeDispatch_FormatLine(); + void executeDispatch_FormatArea(); + void executeDispatch_TextAttributes(); + void executeDispatch_TransformDialog(); + void executeDispatch_ObjectTitleDescription(); + void executeDispatch_RenameObject(); + void executeDispatch_ChangeZOrder( ChartCommandID nId ); + void executeDispatch_FontDialog(); + void executeDispatch_ParagraphDialog(); + + SdrObject* getFirstAdditionalShape(); + SdrObject* getLastAdditionalShape(); + bool isBackwardPossible(); + bool isForwardPossible(); + + ChartController* m_pChartController; +}; + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/StatusBarCommandDispatch.cxx b/chart2/source/controller/main/StatusBarCommandDispatch.cxx new file mode 100644 index 0000000000..e3c1f038b0 --- /dev/null +++ b/chart2/source/controller/main/StatusBarCommandDispatch.cxx @@ -0,0 +1,127 @@ +/* -*- 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 "StatusBarCommandDispatch.hxx" +#include <ObjectNameProvider.hxx> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <ChartModel.hxx> +#include <utility> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace chart +{ + +StatusBarCommandDispatch::StatusBarCommandDispatch( + const Reference< uno::XComponentContext > & xContext, + rtl::Reference<::chart::ChartModel> xModel, + const Reference< view::XSelectionSupplier > & xSelSupp ) : + impl::StatusBarCommandDispatch_Base( xContext ), + m_xChartModel(std::move( xModel )), + m_xSelectionSupplier( xSelSupp ), + m_bIsModified( false ) +{} + +StatusBarCommandDispatch::~StatusBarCommandDispatch() +{} + +void StatusBarCommandDispatch::initialize() +{ + if( m_xChartModel.is()) + { + m_xChartModel->addModifyListener( this ); + } + + if( m_xSelectionSupplier.is()) + { + m_xSelectionSupplier->addSelectionChangeListener( this ); + } +} + +void StatusBarCommandDispatch::fireStatusEvent( + const OUString & rURL, + const Reference< frame::XStatusListener > & xSingleListener /* = 0 */ ) +{ + bool bFireAll( rURL.isEmpty() ); + bool bFireContext( bFireAll || rURL == ".uno:Context" ); + bool bFireModified( bFireAll || rURL == ".uno:ModifiedStatus" ); + + if( bFireContext && m_xChartModel.is()) + { + uno::Any aArg; + aArg <<= ObjectNameProvider::getSelectedObjectText( m_aSelectedOID.getObjectCID(), m_xChartModel ); + fireStatusEventForURL( ".uno:Context", aArg, true, xSingleListener ); + } + if( bFireModified ) + { + uno::Any aArg; + if( m_bIsModified ) + aArg <<= OUString("*"); + fireStatusEventForURL( ".uno:ModifiedStatus", aArg, true, xSingleListener ); + } +} + +// ____ XDispatch ____ +void SAL_CALL StatusBarCommandDispatch::dispatch( + const util::URL& /* URL */, + const Sequence< beans::PropertyValue >& /* Arguments */ ) +{ + // nothing to do here +} + +// ____ WeakComponentImplHelperBase ____ +/// is called when this is disposed +void StatusBarCommandDispatch::disposing(std::unique_lock<std::mutex>& /*rGuard*/) +{ + m_xChartModel.clear(); + m_xSelectionSupplier.clear(); +} + +// ____ XEventListener (base of XModifyListener) ____ +void SAL_CALL StatusBarCommandDispatch::disposing( const lang::EventObject& /* Source */ ) +{ + m_xChartModel.clear(); + m_xSelectionSupplier.clear(); +} + +// ____ XModifyListener ____ +void SAL_CALL StatusBarCommandDispatch::modified( const lang::EventObject& aEvent ) +{ + if( m_xChartModel.is()) + m_bIsModified = m_xChartModel->isModified(); + + CommandDispatch::modified( aEvent ); +} + +// ____ XSelectionChangeListener ____ +void SAL_CALL StatusBarCommandDispatch::selectionChanged( const lang::EventObject& /* aEvent */ ) +{ + if( m_xSelectionSupplier.is()) + m_aSelectedOID = ObjectIdentifier( m_xSelectionSupplier->getSelection() ); + else + m_aSelectedOID = ObjectIdentifier(); + fireAllStatusEvents( nullptr ); +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/StatusBarCommandDispatch.hxx b/chart2/source/controller/main/StatusBarCommandDispatch.hxx new file mode 100644 index 0000000000..ae9dcaf4f6 --- /dev/null +++ b/chart2/source/controller/main/StatusBarCommandDispatch.hxx @@ -0,0 +1,94 @@ +/* -*- 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 . + */ +#pragma once + +#include "CommandDispatch.hxx" +#include <ObjectIdentifier.hxx> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/view/XSelectionChangeListener.hpp> +#include <rtl/ref.hxx> + +namespace com::sun::star::frame { class XModel; } +namespace com::sun::star::util { class XModifiable; } +namespace com::sun::star::view { class XSelectionSupplier; } + +namespace chart +{ + +/** This is a CommandDispatch implementation for all commands the status bar offers + + This class reads the information needed from the XModel passed here. + */ + +namespace impl +{ +typedef ::cppu::ImplInheritanceHelper< + CommandDispatch, + css::view::XSelectionChangeListener > + StatusBarCommandDispatch_Base; +} + +class StatusBarCommandDispatch : public impl::StatusBarCommandDispatch_Base +{ +public: + explicit StatusBarCommandDispatch( + const css::uno::Reference< css::uno::XComponentContext > & xContext, + rtl::Reference<::chart::ChartModel> xModel, + const css::uno::Reference< css::view::XSelectionSupplier > & xSelSupp ); + virtual ~StatusBarCommandDispatch() override; + + // late initialisation, especially for adding as listener + virtual void initialize() override; + +protected: + // ____ XDispatch ____ + virtual void SAL_CALL dispatch( + const css::util::URL& URL, + const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + + // ____ WeakComponentImplHelperBase ____ + /// is called when this is disposed + virtual void disposing(std::unique_lock<std::mutex>& rGuard) override; + + // ____ XModifyListener (override from CommandDispatch) ____ + virtual void SAL_CALL modified( + const css::lang::EventObject& aEvent ) override; + + // ____ XEventListener (base of XModifyListener) ____ + virtual void SAL_CALL disposing( + const css::lang::EventObject& Source ) override; + + virtual void fireStatusEvent( + const OUString & rURL, + const css::uno::Reference< css::frame::XStatusListener > & xSingleListener ) override; + + // ____ XSelectionChangeListener ____ + virtual void SAL_CALL selectionChanged( + const css::lang::EventObject& aEvent ) override; + +private: + rtl::Reference<::chart::ChartModel> m_xChartModel; + css::uno::Reference< css::view::XSelectionSupplier > m_xSelectionSupplier; + bool m_bIsModified; + ObjectIdentifier m_aSelectedOID; +}; + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/ToolbarController.cxx b/chart2/source/controller/main/ToolbarController.cxx new file mode 100644 index 0000000000..17df7c7c8b --- /dev/null +++ b/chart2/source/controller/main/ToolbarController.cxx @@ -0,0 +1,121 @@ +/* -*- 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/. + */ + +#include <ChartToolbarController.hxx> + +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XFramesSupplier.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <sal/log.hxx> + +namespace com::sun::star::uno { class XComponentContext; } + +namespace chart { + +ChartToolbarController::ChartToolbarController(const css::uno::Sequence<css::uno::Any>& rProperties) +{ + for (const auto& rProperty : rProperties) + { + css::beans::PropertyValue aPropValue; + rProperty >>= aPropValue; + if (aPropValue.Name == "Frame") + { + mxFramesSupplier.set(aPropValue.Value, css::uno::UNO_QUERY); + break; + } + } +} + +ChartToolbarController::~ChartToolbarController() +{ +} + +void ChartToolbarController::execute(sal_Int16 /*nKeyModifier*/) +{ +} + +void ChartToolbarController::click() +{ + css::uno::Reference<css::frame::XFrame> xActiveFrame = mxFramesSupplier->getActiveFrame(); + if (!xActiveFrame.is()) + return; + + css::uno::Reference<css::frame::XController> xActiveController = xActiveFrame->getController(); + if (!xActiveController.is()) + return; + + css::uno::Reference<css::frame::XDispatch> xDispatch(xActiveController, css::uno::UNO_QUERY); + if (!xDispatch.is()) + return; + + css::util::URL aURL; + aURL.Path = "FormatSelection"; + xDispatch->dispatch(aURL, css::uno::Sequence<css::beans::PropertyValue>()); +} + +void ChartToolbarController::doubleClick() +{ + SAL_INFO("chart2", "double clicked"); +} + + +css::uno::Reference<css::awt::XWindow> ChartToolbarController::createPopupWindow() +{ + return css::uno::Reference<css::awt::XWindow>(); +} + +css::uno::Reference<css::awt::XWindow> ChartToolbarController::createItemWindow( + const css::uno::Reference<css::awt::XWindow>& /*rParent*/) +{ + return css::uno::Reference<css::awt::XWindow>(); +} + +void ChartToolbarController::statusChanged(const css::frame::FeatureStateEvent& /*rEvent*/) +{ + +} + +void ChartToolbarController::disposing(const css::lang::EventObject& /*rSource*/) +{ +} + +void ChartToolbarController::initialize(const css::uno::Sequence<css::uno::Any>& /*rAny*/) +{ +} + +void ChartToolbarController::update() +{ +} + + +OUString ChartToolbarController::getImplementationName() +{ + return "org.libreoffice.chart2.Chart2ToolboxController"; +} + +sal_Bool ChartToolbarController::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> ChartToolbarController::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ToolbarController" }; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +org_libreoffice_chart2_Chart2ToolboxController(css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const & rProperties) +{ + return cppu::acquire(new ::chart::ChartToolbarController(rProperties)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/UndoActions.cxx b/chart2/source/controller/main/UndoActions.cxx new file mode 100644 index 0000000000..cf2de5eb5a --- /dev/null +++ b/chart2/source/controller/main/UndoActions.cxx @@ -0,0 +1,115 @@ +/* -*- 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 "UndoActions.hxx" +#include "ChartModelClone.hxx" +#include <ChartModel.hxx> + +#include <com/sun/star/lang/DisposedException.hpp> + +#include <svx/svdundo.hxx> + +#include <memory> +#include <utility> + +using namespace ::com::sun::star; + +namespace chart::impl +{ + using ::com::sun::star::lang::DisposedException; + +UndoElement::UndoElement( OUString i_actionString, rtl::Reference<::chart::ChartModel> i_documentModel, std::shared_ptr< ChartModelClone > i_modelClone ) + :m_sActionString(std::move( i_actionString )) + ,m_xDocumentModel(std::move( i_documentModel )) + ,m_pModelClone(std::move( i_modelClone )) +{ +} + +UndoElement::~UndoElement() +{ +} + +void UndoElement::disposing(std::unique_lock<std::mutex>&) +{ + if ( m_pModelClone ) + m_pModelClone->dispose(); + m_pModelClone.reset(); + m_xDocumentModel.clear(); +} + +OUString SAL_CALL UndoElement::getTitle() +{ + return m_sActionString; +} + +void UndoElement::impl_toggleModelState() +{ + // get a snapshot of the current state of our model + auto pNewClone = std::make_shared<ChartModelClone>( m_xDocumentModel, m_pModelClone->getFacet() ); + // apply the previous snapshot to our model + m_pModelClone->applyToModel( m_xDocumentModel ); + // remember the new snapshot, for the next toggle + m_pModelClone = pNewClone; +} + +void SAL_CALL UndoElement::undo( ) +{ + impl_toggleModelState(); +} + +void SAL_CALL UndoElement::redo( ) +{ + impl_toggleModelState(); +} + +// = ShapeUndoElement + +ShapeUndoElement::ShapeUndoElement( std::unique_ptr<SdrUndoAction> xSdrUndoAction ) + :m_xAction( std::move(xSdrUndoAction) ) +{ +} + +ShapeUndoElement::~ShapeUndoElement() +{ +} + +OUString SAL_CALL ShapeUndoElement::getTitle() +{ + if ( !m_xAction ) + throw DisposedException( OUString(), *this ); + return m_xAction->GetComment(); +} + +void SAL_CALL ShapeUndoElement::undo( ) +{ + if ( !m_xAction ) + throw DisposedException( OUString(), *this ); + m_xAction->Undo(); +} + +void SAL_CALL ShapeUndoElement::redo( ) +{ + if ( !m_xAction ) + throw DisposedException( OUString(), *this ); + m_xAction->Redo(); +} + +} // namespace chart::impl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/UndoActions.hxx b/chart2/source/controller/main/UndoActions.hxx new file mode 100644 index 0000000000..a86479e167 --- /dev/null +++ b/chart2/source/controller/main/UndoActions.hxx @@ -0,0 +1,101 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/document/XUndoAction.hpp> + +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <comphelper/compbase.hxx> + +#include <memory> + +namespace com::sun::star::frame { class XModel; } + +class SdrUndoAction; + +namespace chart +{ +class ChartModel; +class ChartModelClone; + +namespace impl +{ + +typedef comphelper::WeakComponentImplHelper< css::document::XUndoAction > UndoElement_TBase; + +class UndoElement final : public UndoElement_TBase +{ +public: + /** creates a new undo action + + @param i_actionString + is the title of the Undo action + @param i_documentModel + is the actual document model which the undo actions operates on + @param i_modelClone + is the cloned model from before the changes, which the Undo action represents, have been applied. + Upon <member>invoking</member>, the clone model is applied to the document model. + */ + UndoElement( OUString i_actionString, + rtl::Reference<::chart::ChartModel> i_documentModel, + std::shared_ptr< ChartModelClone > i_modelClone + ); + virtual ~UndoElement() override; + + UndoElement(const UndoElement&) = delete; + const UndoElement& operator=(const UndoElement&) = delete; + + // XUndoAction + virtual OUString SAL_CALL getTitle() override; + virtual void SAL_CALL undo( ) override; + virtual void SAL_CALL redo( ) override; + + // WeakComponentImplHelper + virtual void disposing(std::unique_lock<std::mutex>&) override; + +private: + void impl_toggleModelState(); + +private: + OUString m_sActionString; + rtl::Reference<::chart::ChartModel> m_xDocumentModel; + std::shared_ptr< ChartModelClone > m_pModelClone; +}; + +typedef comphelper::WeakComponentImplHelper< css::document::XUndoAction > ShapeUndoElement_TBase; +class ShapeUndoElement final : public ShapeUndoElement_TBase +{ +public: + explicit ShapeUndoElement( std::unique_ptr<SdrUndoAction> xSdrUndoAction ); + virtual ~ShapeUndoElement() override; + + // XUndoAction + virtual OUString SAL_CALL getTitle() override; + virtual void SAL_CALL undo( ) override; + virtual void SAL_CALL redo( ) override; + +private: + std::unique_ptr<SdrUndoAction> m_xAction; +}; + +} // namespace impl +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/UndoCommandDispatch.cxx b/chart2/source/controller/main/UndoCommandDispatch.cxx new file mode 100644 index 0000000000..c90fac3f40 --- /dev/null +++ b/chart2/source/controller/main/UndoCommandDispatch.cxx @@ -0,0 +1,146 @@ +/* -*- 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 "UndoCommandDispatch.hxx" +#include <ChartModel.hxx> + +#include <com/sun/star/util/XModifyBroadcaster.hpp> +#include <com/sun/star/document/UndoFailedException.hpp> + +#include <utility> +#include <vcl/svapp.hxx> +#include <comphelper/diagnose_ex.hxx> + +#include <svtools/strings.hrc> +#include <svtools/svtresid.hxx> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace chart +{ + +UndoCommandDispatch::UndoCommandDispatch( + const Reference< uno::XComponentContext > & xContext, + rtl::Reference<::chart::ChartModel> xModel ) : + CommandDispatch( xContext ), + m_xModel(std::move( xModel )) +{ + m_xUndoManager.set( m_xModel->getUndoManager(), uno::UNO_SET_THROW ); +} + +UndoCommandDispatch::~UndoCommandDispatch() +{} + +void UndoCommandDispatch::initialize() +{ + Reference< util::XModifyBroadcaster > xBroadcaster( m_xUndoManager, uno::UNO_QUERY ); + ENSURE_OR_RETURN_VOID( xBroadcaster.is(), "UndoCommandDispatch::initialize: missing modification broadcaster interface!" ); + xBroadcaster->addModifyListener( this ); +} + +void UndoCommandDispatch::fireStatusEvent( + const OUString & rURL, + const Reference< frame::XStatusListener > & xSingleListener /* = 0 */ ) +{ + if( !m_xUndoManager.is() ) + return; + + const bool bFireAll = rURL.isEmpty(); + uno::Any aUndoState, aRedoState, aUndoStrings, aRedoStrings; + if( m_xUndoManager->isUndoPossible()) + aUndoState <<= SvtResId( STR_UNDO ) + m_xUndoManager->getCurrentUndoActionTitle(); + if( m_xUndoManager->isRedoPossible()) + aRedoState <<= SvtResId( STR_REDO ) + m_xUndoManager->getCurrentRedoActionTitle(); + + aUndoStrings <<= m_xUndoManager->getAllUndoActionTitles(); + aRedoStrings <<= m_xUndoManager->getAllRedoActionTitles(); + + if( bFireAll || rURL == ".uno:Undo" ) + fireStatusEventForURL( ".uno:Undo", aUndoState, m_xUndoManager->isUndoPossible(), xSingleListener ); + if( bFireAll || rURL == ".uno:Redo" ) + fireStatusEventForURL( ".uno:Redo", aRedoState, m_xUndoManager->isRedoPossible(), xSingleListener ); + if( bFireAll || rURL == ".uno:GetUndoStrings" ) + fireStatusEventForURL( ".uno:GetUndoStrings", aUndoStrings, true, xSingleListener ); + if( bFireAll || rURL == ".uno:GetRedoStrings" ) + fireStatusEventForURL( ".uno:GetRedoStrings", aRedoStrings, true, xSingleListener ); +} + +// ____ XDispatch ____ +void SAL_CALL UndoCommandDispatch::dispatch( + const util::URL& URL, + const Sequence< beans::PropertyValue >& Arguments ) +{ + if( !m_xUndoManager.is() ) + return; + + // why is it necessary to lock the solar mutex here? + SolarMutexGuard aSolarGuard; + try + { + sal_Int16 nCount( 1 ); + if ( Arguments.hasElements() && Arguments[0].Name == URL.Path ) + Arguments[0].Value >>= nCount; + + while ( nCount-- ) + { + if ( URL.Path == "Undo" ) + m_xUndoManager->undo(); + else + m_xUndoManager->redo(); + } + } + catch( const document::UndoFailedException& ) + { + // silently ignore + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + // \-- +} + +// ____ WeakComponentImplHelperBase ____ +/// is called when this is disposed +void UndoCommandDispatch::disposing(std::unique_lock<std::mutex>& /*rGuard*/) +{ + Reference< util::XModifyBroadcaster > xBroadcaster( m_xUndoManager, uno::UNO_QUERY ); + OSL_ENSURE( xBroadcaster.is(), "UndoCommandDispatch::initialize: missing modification broadcaster interface!" ); + if( xBroadcaster.is() ) + { + xBroadcaster->removeModifyListener( this ); + } + + m_xUndoManager.clear(); + m_xModel.clear(); +} + +// ____ XEventListener (base of XModifyListener) ____ +void SAL_CALL UndoCommandDispatch::disposing( const lang::EventObject& /* Source */ ) +{ + m_xUndoManager.clear(); + m_xModel.clear(); +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/UndoCommandDispatch.hxx b/chart2/source/controller/main/UndoCommandDispatch.hxx new file mode 100644 index 0000000000..f872387c76 --- /dev/null +++ b/chart2/source/controller/main/UndoCommandDispatch.hxx @@ -0,0 +1,69 @@ +/* -*- 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 . + */ +#pragma once + +#include "CommandDispatch.hxx" +#include <rtl/ref.hxx> + +namespace com::sun::star::document { class XUndoManager; } +namespace com::sun::star::frame { class XModel; } + +namespace chart +{ +class ChartModel; + +/** This is a CommandDispatch implementation for Undo and Redo. + */ +class UndoCommandDispatch : public CommandDispatch +{ +public: + explicit UndoCommandDispatch( + const css::uno::Reference< css::uno::XComponentContext > & xContext, + rtl::Reference<::chart::ChartModel> xModel ); + virtual ~UndoCommandDispatch() override; + + // late initialisation, especially for adding as listener + virtual void initialize() override; + +protected: + // ____ XDispatch ____ + virtual void SAL_CALL dispatch( + const css::util::URL& URL, + const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + + // ____ WeakComponentImplHelperBase ____ + /// is called when this is disposed + virtual void disposing(std::unique_lock<std::mutex>& rGuard) override; + + // ____ XEventListener (base of XModifyListener) ____ + virtual void SAL_CALL disposing( + const css::lang::EventObject& Source ) override; + + virtual void fireStatusEvent( + const OUString & rURL, + const css::uno::Reference< css::frame::XStatusListener > & xSingleListener ) override; + +private: + rtl::Reference<::chart::ChartModel> m_xModel; + css::uno::Reference< css::document::XUndoManager > m_xUndoManager; +}; + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/UndoGuard.cxx b/chart2/source/controller/main/UndoGuard.cxx new file mode 100644 index 0000000000..4e870c36d0 --- /dev/null +++ b/chart2/source/controller/main/UndoGuard.cxx @@ -0,0 +1,152 @@ +/* -*- 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 "UndoGuard.hxx" +#include "ChartModelClone.hxx" +#include "UndoActions.hxx" +#include <ChartModel.hxx> + +#include <com/sun/star/document/XUndoManager.hpp> +#include <utility> + +#include <comphelper/diagnose_ex.hxx> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; + +namespace chart +{ + +UndoGuard::UndoGuard( OUString i_undoString, const uno::Reference< document::XUndoManager > & i_undoManager, + const ModelFacet i_facet ) + :m_xUndoManager( i_undoManager ) + ,m_aUndoString(std::move( i_undoString )) + ,m_bActionPosted( false ) +{ + m_xChartModel = dynamic_cast<::chart::ChartModel*>(i_undoManager->getParent().get()); + assert(m_xChartModel); + m_pDocumentSnapshot = std::make_shared<ChartModelClone>( m_xChartModel, i_facet ); +} + +UndoGuard::~UndoGuard() +{ + if ( m_pDocumentSnapshot ) + discardSnapshot(); +} + +void UndoGuard::commit() +{ + if ( !m_bActionPosted && m_pDocumentSnapshot ) + { + try + { + const Reference< document::XUndoAction > xAction( new impl::UndoElement( m_aUndoString, m_xChartModel, m_pDocumentSnapshot ) ); + m_pDocumentSnapshot.reset(); // don't dispose, it's data went over to the UndoElement + m_xUndoManager->addUndoAction( xAction ); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + } + m_bActionPosted = true; +} + +void UndoGuard::rollback() +{ + ENSURE_OR_RETURN_VOID( !!m_pDocumentSnapshot, "no snapshot!" ); + m_pDocumentSnapshot->applyToModel( m_xChartModel ); + discardSnapshot(); +} + +void UndoGuard::discardSnapshot() +{ + ENSURE_OR_RETURN_VOID( !!m_pDocumentSnapshot, "no snapshot!" ); + m_pDocumentSnapshot->dispose(); + m_pDocumentSnapshot.reset(); +} + +UndoLiveUpdateGuard::UndoLiveUpdateGuard( const OUString& i_undoString, const uno::Reference< document::XUndoManager >& i_undoManager ) + :UndoGuard( i_undoString, i_undoManager, E_MODEL ) +{ +} + +UndoLiveUpdateGuard::~UndoLiveUpdateGuard() +{ + if ( !isActionPosted() ) + rollback(); +} + +UndoLiveUpdateGuardWithData::UndoLiveUpdateGuardWithData( + const OUString& i_undoString, const uno::Reference< document::XUndoManager >& i_undoManager ) + :UndoGuard( i_undoString, i_undoManager, E_MODEL_WITH_DATA ) +{ +} + +UndoLiveUpdateGuardWithData::~UndoLiveUpdateGuardWithData() +{ + if ( !isActionPosted() ) + rollback(); +} + +UndoGuardWithSelection::UndoGuardWithSelection( + const OUString& i_undoString, const uno::Reference< document::XUndoManager >& i_undoManager ) + :UndoGuard( i_undoString, i_undoManager, E_MODEL_WITH_SELECTION ) +{ +} + +UndoGuardWithSelection::~UndoGuardWithSelection() +{ + if ( !isActionPosted() ) + rollback(); +} + +HiddenUndoContext::HiddenUndoContext( const Reference< document::XUndoManager > & i_undoManager ) + :m_xUndoManager( i_undoManager ) +{ + ENSURE_OR_THROW( m_xUndoManager.is(), "invalid undo manager!" ); + try + { + m_xUndoManager->enterHiddenUndoContext(); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + m_xUndoManager.clear(); + // prevents the leaveUndoContext in the dtor + } +} + +HiddenUndoContext::~HiddenUndoContext() +{ + try + { + if ( m_xUndoManager.is() ) + m_xUndoManager->leaveUndoContext(); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/controller/main/UndoGuard.hxx b/chart2/source/controller/main/UndoGuard.hxx new file mode 100644 index 0000000000..90443a247a --- /dev/null +++ b/chart2/source/controller/main/UndoGuard.hxx @@ -0,0 +1,115 @@ +/* -*- 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 . + */ +#pragma once + +#include "ChartModelClone.hxx" + +#include <rtl/ustring.hxx> + +#include <memory> + +namespace com::sun::star::document { class XUndoManager; } +namespace com::sun::star::frame { class XModel; } + +namespace chart +{ + +/** A guard which does nothing, unless you explicitly call commitAction. In particular, in its destructor, it + does neither auto-commit nor auto-rollback the model changes. + */ +class UndoGuard +{ +public: + explicit UndoGuard( + OUString i_undoMessage, + const css::uno::Reference< css::document::XUndoManager > & i_undoManager, + const ModelFacet i_facet = E_MODEL + ); + ~UndoGuard(); + + void commit(); + void rollback(); + +protected: + bool isActionPosted() const { return m_bActionPosted; } + +private: + void discardSnapshot(); + +private: + rtl::Reference<::chart::ChartModel> m_xChartModel; + const css::uno::Reference< css::document::XUndoManager > m_xUndoManager; + + std::shared_ptr< ChartModelClone > m_pDocumentSnapshot; + OUString m_aUndoString; + bool m_bActionPosted; +}; + +/** A guard which, in its destructor, restores the model state it found in the constructor. If + <member>commitAction</member> is called inbetween, the restoration is not performed. + */ +class UndoLiveUpdateGuard : public UndoGuard +{ +public: + explicit UndoLiveUpdateGuard( + const OUString& i_undoMessage, + const css::uno::Reference< css::document::XUndoManager > & i_undoManager + ); + ~UndoLiveUpdateGuard(); +}; + +/** Same as UndoLiveUpdateGuard but with additional storage of the chart's data. + Only use this if the data has internal data. + */ +class UndoLiveUpdateGuardWithData : + public UndoGuard +{ +public: + explicit UndoLiveUpdateGuardWithData( + const OUString& i_undoMessage, + const css::uno::Reference< css::document::XUndoManager > & i_undoManager + ); + ~UndoLiveUpdateGuardWithData(); +}; + +class UndoGuardWithSelection : public UndoGuard +{ +public: + explicit UndoGuardWithSelection( + const OUString& i_undoMessage, + const css::uno::Reference< css::document::XUndoManager > & i_undoManager + ); + virtual ~UndoGuardWithSelection(); +}; + +class HiddenUndoContext +{ +public: + explicit HiddenUndoContext( + const css::uno::Reference< css::document::XUndoManager > & i_undoManager + ); + ~HiddenUndoContext(); + +private: + css::uno::Reference< css::document::XUndoManager > m_xUndoManager; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |