| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| /* |
| * This file is part of the Collabora Office 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 <ResId.hxx> |
| #include <dlg_DataSource.hxx> |
| #include <ChartModel.hxx> |
| #include <ChartType.hxx> |
| #include <ControllerCommandDispatch.hxx> |
| #include <DataSeries.hxx> |
| #include <Diagram.hxx> |
| #include <strings.hrc> |
| #include <ChartViewHelper.hxx> |
| |
| #include <ChartWindow.hxx> |
| #include <chartview/DrawModelWrapper.hxx> |
| #include <DrawViewWrapper.hxx> |
| #include <ObjectIdentifier.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 <BaseCoordinateSystem.hxx> |
| |
| #include <com/sun/star/frame/XController2.hpp> |
| #include <com/sun/star/util/CloseVetoException.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/dndlistenercontainer.hxx> |
| #include <vcl/svapp.hxx> |
| #include <vcl/weld.hxx> |
| #include <osl/mutex.hxx> |
| #include <comphelper/lok.hxx> |
| |
| #include <sfx2/sidebar/SidebarController.hxx> |
| #include <com/sun/star/frame/XLayoutManager.hpp> |
| |
| // this is needed to properly destroy the unique_ptr to the AcceleratorExecute |
| // object in the DTOR |
| #include <svtools/acceleratorexecute.hxx> |
| #include <svx/ActionDescriptionProvider.hxx> |
| #include <comphelper/diagnose_ex.hxx> |
| |
| #include <editeng/eerdll.hxx> |
| #include <editeng/editrids.hrc> |
| #include <editeng/editids.hrc> |
| #include <editeng/udlnitem.hxx> |
| #include <svx/svdoutl.hxx> |
| #include <editeng/outliner.hxx> |
| #include <editeng/postitem.hxx> |
| #include <editeng/wghtitem.hxx> |
| #include <editeng/crossedoutitem.hxx> |
| #include <editeng/fontitem.hxx> |
| #include <editeng/fhgtitem.hxx> |
| #include <editeng/shdditem.hxx> |
| #include <editeng/shaditem.hxx> |
| #include <editeng/escapementitem.hxx> |
| #include <editeng/colritem.hxx> |
| #include <editeng/kernitem.hxx> |
| #include <editeng/flstitem.hxx> |
| |
| #include <dlg_Theme.hxx> |
| #include <ObjectHierarchy.hxx> |
| #include <svx/ChartThemeType.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 u"Chart"_ustr; |
| |
| OUString aCID; |
| aAny >>= aCID; |
| |
| if (aCID.isEmpty()) |
| return u"Chart"_ustr; |
| |
| ObjectType eObjectID = ObjectIdentifier::getObjectType(aCID); |
| switch (eObjectID) |
| { |
| case OBJECTTYPE_DATA_SERIES: |
| return u"Series"_ustr; |
| case OBJECTTYPE_DATA_ERRORS_X: |
| case OBJECTTYPE_DATA_ERRORS_Y: |
| case OBJECTTYPE_DATA_ERRORS_Z: |
| return u"ErrorBar"_ustr; |
| case OBJECTTYPE_AXIS: |
| return u"Axis"_ustr; |
| case OBJECTTYPE_GRID: |
| return u"Grid"_ustr; |
| case OBJECTTYPE_DIAGRAM: |
| { |
| rtl::Reference<ChartType> xChartType = getChartType(getChartModel()); |
| if (xChartType.is() && xChartType->getChartType() == "com.sun.star.chart2.PieChartType") |
| return u"ChartElements"_ustr; |
| break; |
| } |
| case OBJECTTYPE_DATA_CURVE: |
| case OBJECTTYPE_DATA_AVERAGE_LINE: |
| return u"Trendline"_ustr; |
| case OBJECTTYPE_TITLE: |
| return u"ChartTitle"_ustr; |
| case OBJECTTYPE_LEGEND: |
| return u"ChartLegend"_ustr; |
| case OBJECTTYPE_DATA_LABEL: |
| case OBJECTTYPE_DATA_LABELS: |
| return u"ChartLabel"_ustr; |
| default: |
| break; |
| } |
| |
| return u"Chart"_ustr; |
| } |
| |
| // 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()); |
| } |
| |
| // Notify context change unconditionally so that COKit clients |
| // (e.g. the notebookbar) learn about chart activation even when |
| // no sidebar is available. |
| mpSelectionChangeHandler->selectionChanged(css::lang::EventObject()); |
| |
| //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( u"LayoutManager"_ustr ) >>= xLayoutManager; |
| if ( xLayoutManager.is() ) |
| { |
| xLayoutManager->lock(); |
| xLayoutManager->requestElement( u"private:resource/menubar/menubar"_ustr ); |
| //@todo: createElement should become unnecessary, remove when #i79198# is fixed |
| xLayoutManager->createElement( u"private:resource/toolbar/standardbar"_ustr ); |
| xLayoutManager->requestElement( u"private:resource/toolbar/standardbar"_ustr ); |
| //@todo: createElement should become unnecessary, remove when #i79198# is fixed |
| xLayoutManager->createElement( u"private:resource/toolbar/toolbar"_ustr ); |
| xLayoutManager->requestElement( u"private:resource/toolbar/toolbar"_ustr ); |
| |
| // #i12587# support for shapes in chart |
| xLayoutManager->createElement( u"private:resource/toolbar/drawbar"_ustr ); |
| xLayoutManager->requestElement( u"private:resource/toolbar/drawbar"_ustr ); |
| |
| xLayoutManager->requestElement( u"private:resource/statusbar/statusbar"_ustr ); |
| 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 |
| getChartModel()->triggerRangeHighlighting(); |
| |
| impl_initializeAccessible(); |
| |
| 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(), and also for which ControllerCommandDispatch::commandHandled() |
| // gives true. That means, for those commands dispatch() is called here at the ChartController. |
| m_aDispatchContainer.setChartDispatch( pDispatch, impl_getAvailableCommands() ); |
| |
| rtl::Reference<DrawCommandDispatch> pDrawDispatch = new DrawCommandDispatch( m_xCC, this ); |
| pDrawDispatch->initialize(); |
| m_aDispatchContainer.setDrawCommandDispatch( pDrawDispatch.get() ); |
| |
| rtl::Reference<ShapeController> pShapeController = new ShapeController( m_xCC, this ); |
| pShapeController->initialize(); |
| m_aDispatchContainer.setShapeController( pShapeController.get() ); |
| aGuard.clear(); |
| |
| #ifdef TEST_ENABLE_MODIFY_LISTENER |
| if( aNewModelRef->getModel().is()) |
| aNewModelRef->getModel()->addModifyListener( this ); |
| #endif |
| |
| // #i119999# Do not do this per default to allow the user to deselect the chart OLE with a single press to ESC |
| // select chart area per default: |
| // select( uno::Any( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, OUString() ) ) ); |
| |
| rtl::Reference< ChartModel > xFact = getChartModel(); |
| if( xFact.is()) |
| { |
| m_xChartView = xFact->createChartView(); |
| 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() |
| { |
| rtl::Reference<::chart::ChartModel> xModel = getChartModel(); |
| if (!xModel) |
| return nullptr; |
| return xModel->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::COKit::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()) |
| { |
| rtl::Reference< ChartModel > 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( u"private:resource/statusbar/statusbar"_ustr ); |
| xLM->requestElement( u"private:resource/statusbar/statusbar"_ustr ); |
| } |
| } |
| } |
| |
| // 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) |
| executeDispatch_FillColor(rArgs[0].Value); |
| } |
| else if(aCommand == "XLineColor") |
| { |
| if (rArgs.getLength() > 0) |
| executeDispatch_LineColor(rArgs[0].Value); |
| } |
| else if(aCommand == "LineWidth") |
| { |
| if (rArgs.getLength() > 0) |
| executeDispatch_LineWidth(rArgs[0].Value); |
| } |
| 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(); |
| else if( aCommand == "ManageThemes") |
| this->executeDispatch_ManageThemes(); |
| else if (aCommand == "SelectTheme") |
| { |
| this->executeDispatch_ManageThemes(true); |
| // Todo: try to make a popup window only |
| // it did something but not displayed anything. |
| |
| //static std::shared_ptr<sidebar::ChartThemeControl> pThemeControl |
| // = std::make_shared<sidebar::ChartThemeControl>(m_xCC); |
| //assert(pThemeControl); |
| //std::shared_ptr<sidebar::IThemeHandler> mxThemeWrapper |
| // = sidebar::createThemeWrapper(getChartModel(), pThemeControl.get(), this); |
| //pThemeControl->setThemeHandler(mxThemeWrapper); |
| //pThemeControl->weldPopupWindow(); |
| |
| // it need addition includes: |
| //#include <../sidebar/ChartThemePanel.hxx> |
| //#include <../sidebar/ChartThemeControl.hxx> |
| |
| // it need createThemeWrapper a ChartThemePanel.cxx: |
| //std::shared_ptr<sidebar::IThemeHandler> createThemeWrapper( |
| // rtl::Reference<ChartModel> xModel, ChartThemeControl * pControl, |
| // ChartController * pController) |
| //{ |
| // return std::make_shared<sidebar::ThemeWrapper>(xModel, pControl, pController); |
| //} |
| |
| // and in a ChartThemePanel.hxx: |
| //struct IThemeHandler; |
| //class ChartThemeControl; |
| // |
| //std::shared_ptr<sidebar::IThemeHandler> createThemeWrapper( |
| // rtl::Reference<ChartModel> xModel, ChartThemeControl * pControl, |
| // ChartController * pController); |
| } |
| else if (aCommand == "ChartSaveToNewTheme") |
| { |
| this->executeDispatch_SaveToNewTheme(); |
| } |
| //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 ( aCommand == "FontDialog" ) |
| this->impl_ShapeControllerDispatch(rURL, rArgs); |
| 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 == "Bold" || aCommand == "CharFontName" || aCommand == "FontHeight" |
| || aCommand == "Italic" || aCommand == "Underline" || aCommand == "Strikeout" |
| || aCommand == "Shadowed" || aCommand == "Color" || aCommand == "FontColor" |
| || aCommand == "Grow" || aCommand == "Shrink" || aCommand == "ResetAttributes" |
| || aCommand == "SuperScript" || aCommand == "SubScript" |
| || aCommand == "Spacing" |
| ) |
| { |
| try |
| { |
| OUString aCID(m_aSelection.getSelectedCID()); |
| rtl::Reference<::chart::ChartModel> xChartModel = getChartModel(); |
| if (xChartModel.is()) |
| { |
| // if the selected is title... we should get the text properties instead... |
| // or the selected text properties |
| std::vector<Reference<beans::XPropertySet>> xProperties; |
| xProperties.emplace(xProperties.end(), |
| ObjectIdentifier::getObjectPropertySet(aCID, xChartModel)); |
| |
| if (ObjectIdentifier::getObjectType(aCID) == OBJECTTYPE_TITLE) |
| { |
| Reference<chart2::XTitle> xTitle(xProperties[0], uno::UNO_QUERY); |
| if (xTitle.is()) |
| { |
| OutlinerView* pOutlinerView = nullptr; |
| SdrOutliner* pOutliner = nullptr; |
| if (m_pDrawViewWrapper) |
| { |
| pOutlinerView = m_pDrawViewWrapper->GetTextEditOutlinerView(); |
| pOutliner = m_pDrawViewWrapper->GetTextEditOutliner(); |
| } |
| |
| // if the Title is in edit mode |
| if (pOutlinerView && pOutliner) |
| { |
| xProperties.pop_back(); |
| |
| SolarMutexGuard aSolarGuard; |
| SfxItemSet aEditAttr(pOutlinerView->GetAttribs()); |
| SfxItemSet aNewAttr(pOutliner->GetEmptyItemSet()); |
| |
| if (aCommand == "CharFontName") |
| { |
| SvxFontItem aFont(EE_CHAR_FONTINFO); |
| aFont.PutValue(rArgs[0].Value, 0); |
| aNewAttr.Put(aFont); |
| } |
| else if (aCommand == "FontHeight") |
| { |
| SvxFontHeightItem aFontHeight(260, 100, EE_CHAR_FONTHEIGHT); |
| aFontHeight.PutValue(rArgs[0].Value, MID_FONTHEIGHT); |
| aNewAttr.Put(aFontHeight); |
| |
| } |
| else if (aCommand == "Bold") |
| { |
| FontWeight eFW = aEditAttr.Get(EE_CHAR_WEIGHT).GetWeight(); |
| aNewAttr.Put(SvxWeightItem(eFW == WEIGHT_NORMAL ? WEIGHT_BOLD |
| : WEIGHT_NORMAL, |
| EE_CHAR_WEIGHT)); |
| } |
| else if (aCommand == "Italic") |
| { |
| FontItalic eFI = aEditAttr.Get(EE_CHAR_ITALIC).GetPosture(); |
| aNewAttr.Put(SvxPostureItem(eFI == ITALIC_NORMAL ? ITALIC_NONE |
| : ITALIC_NORMAL, |
| EE_CHAR_ITALIC)); |
| } |
| else if (aCommand == "Underline") |
| { |
| sal_Int16 nFontUnderline = 0; |
| sal_Int32 nFontUnderline32 = 0; |
| if (!(rArgs[0].Value >>= nFontUnderline) |
| && (rArgs[0].Value >>= nFontUnderline32)) |
| { |
| aNewAttr.Put(SvxUnderlineItem(FontLineStyle(nFontUnderline32), |
| EE_CHAR_UNDERLINE)); |
| } |
| else |
| { |
| FontLineStyle eFU |
| = aEditAttr.Get(EE_CHAR_UNDERLINE).GetLineStyle(); |
| aNewAttr.Put(SvxUnderlineItem( |
| eFU == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, |
| EE_CHAR_UNDERLINE)); |
| } |
| } |
| else if (aCommand == "Strikeout") |
| { |
| FontStrikeout eFSO |
| = aEditAttr.Get(EE_CHAR_STRIKEOUT).GetStrikeout(); |
| aNewAttr.Put(SvxCrossedOutItem( |
| eFSO == STRIKEOUT_SINGLE ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, |
| EE_CHAR_STRIKEOUT)); |
| } |
| else if (aCommand == "Shadowed") |
| { |
| bool bShadow = aEditAttr.Get(EE_CHAR_SHADOW).GetValue(); |
| aNewAttr.Put(SvxShadowedItem(!bShadow, EE_CHAR_SHADOW)); |
| } |
| else if (aCommand == "Color" || aCommand == "FontColor") |
| { |
| SvxColorItem aColor(EE_CHAR_COLOR); |
| aColor.PutValue(rArgs[0].Value, MID_COLOR_RGB); |
| aNewAttr.Put(aColor); |
| } |
| else if (aCommand == "Grow" || aCommand == "Shrink") |
| { |
| bool bGrow = (aCommand == "Grow"); |
| const SfxObjectShell* pObjSh = SfxObjectShell::Current(); |
| const SvxFontListItem* pFontListItem |
| = pObjSh ? pObjSh->GetItem(SID_ATTR_CHAR_FONTLIST) : nullptr; |
| const FontList* pFontList |
| = pFontListItem ? pFontListItem->GetFontList() : nullptr; |
| pOutlinerView->GetEditView().ChangeFontSize(bGrow, pFontList); |
| } |
| else if (aCommand == "ResetAttributes") |
| { |
| aNewAttr.Put(SvxFontItem(EE_CHAR_FONTINFO)); |
| aNewAttr.Put(SvxFontHeightItem(260, 100, EE_CHAR_FONTHEIGHT)); |
| aNewAttr.Put(SvxWeightItem(WEIGHT_NORMAL, EE_CHAR_WEIGHT)); |
| aNewAttr.Put(SvxPostureItem(ITALIC_NONE, EE_CHAR_ITALIC)); |
| aNewAttr.Put(SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE)); |
| aNewAttr.Put(SvxCrossedOutItem(STRIKEOUT_NONE, EE_CHAR_STRIKEOUT)); |
| aNewAttr.Put(SvxShadowedItem(false, EE_CHAR_SHADOW)); |
| aNewAttr.Put(SvxColorItem(Color(0), EE_CHAR_COLOR)); |
| aNewAttr.Put(SvxKerningItem(0, EE_CHAR_KERNING)); |
| aNewAttr.Put( |
| SvxEscapementItem(SvxEscapement::Off, EE_CHAR_ESCAPEMENT)); |
| } |
| else if (aCommand == "Spacing") |
| { |
| sal_Int16 nKerning = 0; |
| rArgs[0].Value >>= nKerning; |
| aNewAttr.Put(SvxKerningItem(nKerning, EE_CHAR_KERNING)); |
| } |
| else if (aCommand == "SuperScript" || aCommand == "SubScript") |
| { |
| SvxEscapement eCmd = aCommand == "SuperScript" |
| ? SvxEscapement::Superscript |
| : SvxEscapement::Subscript; |
| SvxEscapement eFE |
| = aEditAttr.Get(EE_CHAR_ESCAPEMENT).GetEscapement(); |
| aNewAttr.Put(SvxEscapementItem( |
| eFE == eCmd ? SvxEscapement::Off : eCmd, EE_CHAR_ESCAPEMENT)); |
| } |
| |
| // Set the Attributes to the Editview |
| pOutlinerView->SetAttribs(aNewAttr); |
| |
| // update the sidebar |
| ControllerCommandDispatch* pCommandDispatch |
| = m_aDispatchContainer.getChartDispatcher(); |
| if (pCommandDispatch) |
| { |
| pCommandDispatch->updateAndFireStatus(); |
| } |
| } |
| else |
| { |
| // if the Title is not in edit mode |
| const Sequence<Reference<chart2::XFormattedString>> aStrings( |
| xTitle->getText()); |
| xProperties.pop_back(); |
| for (const auto& aString : aStrings) |
| { |
| Reference<beans::XPropertySet> xTitlePropSet(aString, uno::UNO_QUERY); |
| xProperties.push_back(xTitlePropSet); |
| } |
| } |
| } |
| } |
| bool bAllPropertiesExist = (xProperties.size() > 0); |
| for (const auto& xProperty : xProperties) |
| { |
| if (!xProperty.is()) |
| bAllPropertiesExist = false; |
| } |
| if (bAllPropertiesExist) |
| { |
| SolarMutexGuard aSolarGuard; |
| UndoGuard aUndoGuard(EditResId(RID_OUTLUNDO_ATTR), m_xUndoManager); |
| if (aCommand == "Bold") |
| { |
| executeDispatch_FontBold(xProperties); |
| } |
| else if (aCommand == "CharFontName") |
| { |
| executeDispatch_FontName(xProperties, rArgs); |
| } |
| else if (aCommand == "FontHeight") |
| { |
| executeDispatch_FontHeight(xProperties, rArgs); |
| } |
| else if (aCommand == "Italic") |
| { |
| executeDispatch_FontItalic(xProperties); |
| } |
| else if (aCommand == "Underline") |
| { |
| executeDispatch_FontUnderline(xProperties, rArgs); |
| } |
| else if (aCommand == "Strikeout") |
| { |
| executeDispatch_FontStrikeout(xProperties); |
| } |
| else if (aCommand == "Shadowed") |
| { |
| executeDispatch_FontShadowed(xProperties); |
| } |
| else if (aCommand == "Color" || aCommand == "FontColor") |
| { |
| executeDispatch_FontColor(xProperties, rArgs); |
| } |
| else if (aCommand == "Grow") |
| { |
| executeDispatch_FontGrow(xProperties); |
| } |
| else if (aCommand == "Shrink") |
| { |
| executeDispatch_FontShrink(xProperties); |
| } |
| else if (aCommand == "ResetAttributes") |
| { |
| executeDispatch_FontReset(xProperties); |
| } |
| else if (aCommand == "Spacing") |
| { |
| executeDispatch_FontSpacing(xProperties, rArgs); |
| } |
| else if (aCommand == "SuperScript") |
| { |
| executeDispatch_FontSuperScript(xProperties); |
| } |
| else if (aCommand == "SubScript") |
| { |
| executeDispatch_FontSubScript(xProperties); |
| } |
| aUndoGuard.commit(); |
| } |
| } |
| } |
| catch (const uno::Exception&) |
| { |
| DBG_UNHANDLED_EXCEPTION("chart2"); |
| } |
| |
| } |
| } |
| |
| 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() |
| { |
| auto xUndoGuard = std::make_shared<UndoLiveUpdateGuard>(SchResId(STR_ACTION_EDIT_CHARTTYPE), |
| m_xUndoManager); |
| |
| SolarMutexGuard aSolarGuard; |
| //prepare and open dialog |
| auto aDlg = std::make_shared<ChartTypeDialog>(GetChartFrame(), getChartModel()); |
| weld::DialogController::runAsync(aDlg, [this, xUndoGuard=std::move(xUndoGuard)](int nResult) { |
| if (nResult == RET_OK) |
| { |
| impl_adaptDataSeriesAutoResize(); |
| xUndoGuard->commit(); |
| } |
| }); |
| } |
| |
| void ChartController::executeDispatch_ManageThemes(bool bSelectOnly) |
| { |
| SolarMutexGuard aSolarGuard; |
| auto xUndoGuard = std::make_shared<UndoGuard>( |
| ActionDescriptionProvider::createDescription(ActionDescriptionProvider::ActionType::Insert, |
| SchResId(STR_OBJECT_TITLES)), |
| m_xUndoManager); |
| |
| try |
| { |
| auto aDlg = std::make_shared<SchThemeDlg>(GetChartFrame(), this, bSelectOnly); |
| weld::DialogController::runAsync( |
| aDlg, |
| [aDlg, xUndoGuard = std::move(xUndoGuard)](int nResult) { |
| if (nResult == RET_OK) |
| { |
| xUndoGuard->commit(); |
| } |
| }); |
| } |
| catch (const uno::RuntimeException&) |
| { |
| TOOLS_WARN_EXCEPTION("chart2", ""); |
| } |
| } |
| |
| void ChartController::executeDispatch_SaveToNewTheme() |
| { |
| ChartThemesType& aChartTypes = ChartThemesType::getInstance(); |
| int nIndex = aChartTypes.m_aThemes.size(); |
| // maximum 256 chart styles |
| // Todo: maybe we could figure out some other way to limit this. |
| if (nIndex >= 256) |
| { |
| return; |
| } |
| saveTheme(nIndex); |
| } |
| |
| 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< css::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); |
| } |
| } |
| auto xUndoGuard = std::make_shared<UndoLiveUpdateGuard>(SchResId(STR_ACTION_EDIT_DATA_RANGES), |
| m_xUndoManager); |
| SolarMutexGuard aSolarGuard; |
| auto aDlg = std::make_shared<DataSourceDialog>(GetChartFrame(), xChartDoc); |
| weld::DialogController::runAsync(aDlg, [this, xUndoGuard=std::move(xUndoGuard)](int nResult) { |
| if (nResult == RET_OK) |
| { |
| impl_adaptDataSeriesAutoResize(); |
| xUndoGuard->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([this](std::unique_ptr<SdrUndoAction> pUndoAction){ |
| NotifyUndoActionHdl(std::move(pUndoAction)); |
| }); |
| } |
| } |
| 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()); |
| } |
| } |
| |
| rtl::Reference<AccessibleChartView> ChartController::CreateAccessible() |
| { |
| #if !ENABLE_WASM_STRIP_ACCESSIBILITY |
| rtl::Reference< AccessibleChartView > xResult = new AccessibleChartView( GetDrawViewWrapper() ); |
| impl_initializeAccessible( *xResult ); |
| return xResult; |
| #else |
| return {}; |
| #endif |
| } |
| |
| void ChartController::impl_invalidateAccessible() |
| { |
| #if !ENABLE_WASM_STRIP_ACCESSIBILITY |
| SolarMutexGuard aGuard; |
| auto pChartWindow(GetChartWindow()); |
| if( pChartWindow ) |
| { |
| rtl::Reference<comphelper::OAccessible> pAccessible = pChartWindow->GetAccessible(false); |
| if (pAccessible.is()) |
| { |
| //empty arguments -> invalid accessible |
| dynamic_cast<AccessibleChartView&>(*pAccessible).initialize(); |
| } |
| } |
| #endif |
| } |
| void ChartController::impl_initializeAccessible() |
| { |
| #if !ENABLE_WASM_STRIP_ACCESSIBILITY |
| SolarMutexGuard aGuard; |
| auto pChartWindow(GetChartWindow()); |
| if( !pChartWindow ) |
| return; |
| rtl::Reference<comphelper::OAccessible> pInit = pChartWindow->GetAccessible(false); |
| if (pInit.is()) |
| impl_initializeAccessible(dynamic_cast<AccessibleChartView&>(*pInit)); |
| #endif |
| } |
| #if !ENABLE_WASM_STRIP_ACCESSIBILITY |
| void ChartController::impl_initializeAccessible( AccessibleChartView& rAccChartView ) |
| { |
| SolarMutexGuard aGuard; |
| |
| uno::Reference< XAccessible > xParent; |
| |
| ChartWindow* pChartWindow = GetChartWindow(); |
| if( pChartWindow ) |
| xParent.set(pChartWindow->GetAccessibleParent()); |
| |
| rAccChartView.initialize(*this, getChartModel(), m_xChartView, xParent, pChartWindow); |
| } |
| #else |
| void ChartController::impl_initializeAccessible( AccessibleChartView& /* rAccChartView */) {} |
| #endif |
| |
| const o3tl::sorted_vector< std::u16string_view >& ChartController::impl_getAvailableCommands() |
| { |
| static const o3tl::sorted_vector< std::u16string_view > s_AvailableCommands { |
| // toolbar commands |
| u"ChartElementSelector", |
| u"ManageThemes", |
| u"SelectTheme", |
| u"ChartSaveToNewTheme", |
| |
| // LOK commands |
| u"LOKSetTextSelection", u"LOKTransform", |
| }; |
| return s_AvailableCommands; |
| } |
| |
| ViewElementListProvider ChartController::getViewElementListProvider() |
| { |
| return ViewElementListProvider(m_pDrawModelWrapper.get()); |
| } |
| |
| static std::set<OUString>& lcl_GetPropertyNameVec() |
| { |
| static std::set<OUString> aFontPropertyNameVec |
| { |
| "CharFontName", |
| "CharFontFamily", |
| "CharFontStyleName", |
| "CharFontCharSet", |
| "CharFontPitch", |
| "CharPosture", |
| "CharWeight", |
| "CharHeight", |
| |
| "CharFontNameAsian", |
| "CharFontFamilyAsian", |
| "CharFontStyleNameAsian", |
| "CharFontCharSetAsian", |
| "CharFontPitchAsian", |
| "CharPostureAsian", |
| "CharWeightAsian", |
| "CharHeightAsian", |
| |
| "CharFontNameComplex", |
| "CharFontFamilyComplex", |
| "CharFontStyleNameComplex", |
| "CharFontCharSetComplex", |
| "CharFontPitchComplex", |
| "CharPostureComplex", |
| "CharWeightComplex", |
| "CharHeightComplex", |
| |
| "CharUnderline", |
| "CharUnderlineColor", |
| "CharUnderlineHasColor", |
| "CharStrikeout", |
| "CharOverline", |
| "CharOverlineColor", |
| "CharOverlineHasColor", |
| |
| "CharColor", |
| "CharKerning", |
| "CharShadowed", |
| "CharRelief", |
| "CharContoured", |
| "CharEmphasis", |
| //some non font related but needed property |
| "AnchorPosition" |
| }; |
| return aFontPropertyNameVec; |
| } |
| |
| static void lcl_SetOrGetThemeToOrFromElement(bool bSet, ChartThemeType& aTheme, |
| ObjectHierarchy& aHierarchy, |
| ObjectIdentifier& aElement, |
| rtl::Reference<::chart::ChartModel>& xCModel) |
| { |
| const OUString& aCID = aElement.getObjectCID(); |
| ObjectType aElementType = aElement.getObjectType(); |
| |
| ChartThemeElementID nElementID = ChartThemeElementID::Nothing; |
| switch (aElementType) |
| { |
| case OBJECTTYPE_TITLE: |
| { |
| size_t nLastSign = aCID.lastIndexOf(u":Title="); |
| if (nLastSign != std::u16string_view::npos) |
| nElementID = ChartThemeElementID::SubTitle; |
| else |
| nElementID = ChartThemeElementID::Title; |
| } |
| break; |
| case OBJECTTYPE_LEGEND: |
| nElementID = ChartThemeElementID::Legend; |
| break; |
| case OBJECTTYPE_AXIS: |
| { |
| nElementID = ChartThemeElementID::Axis; |
| size_t nLastSign = aCID.lastIndexOf(u":Axis="); |
| if (nLastSign != std::u16string_view::npos) |
| { |
| auto nIndex = aCID[nLastSign + 6]; |
| switch (nIndex) |
| { |
| case '1': |
| nElementID = ChartThemeElementID::AxisY; |
| break; |
| case '2': |
| nElementID = ChartThemeElementID::AxisZ; |
| break; |
| } |
| } |
| } |
| break; |
| case OBJECTTYPE_DATA_LABEL: |
| case OBJECTTYPE_DATA_LABELS: |
| case OBJECTTYPE_DATA_SERIES: |
| nElementID = ChartThemeElementID::Label; |
| break; |
| // Todo: we may want to save other element types too |
| default: |
| nElementID = ChartThemeElementID::Nothing; |
| break; |
| } |
| |
| if (nElementID != ChartThemeElementID::Nothing) |
| { |
| // Get the actual chart element properties. |
| // We want to save it, or overwrite it later. |
| std::vector<Reference<beans::XPropertySet>> xProperties; |
| xProperties.emplace(xProperties.end(), |
| ObjectIdentifier::getObjectPropertySet(aCID, xCModel)); |
| |
| if (aElementType == OBJECTTYPE_TITLE) |
| { |
| Reference<chart2::XTitle> xTitle(xProperties[0], uno::UNO_QUERY); |
| if (xTitle.is()) |
| { |
| const Sequence<Reference<chart2::XFormattedString>> aStrings(xTitle->getText()); |
| xProperties.pop_back(); |
| for (int i = 0; i < aStrings.getLength(); i++) |
| { |
| Reference<beans::XPropertySet> xTitlePropSet(aStrings[i], uno::UNO_QUERY); |
| xProperties.push_back(xTitlePropSet); |
| } |
| } |
| } |
| |
| // If we set the style of the chart .. we clear the customized data point properties |
| if (bSet && nElementID == ChartThemeElementID::Label) |
| { |
| rtl::Reference<DataSeries> xSeries( |
| ObjectIdentifier::getDataSeriesForCID(aCID, xCModel)); |
| if (xSeries.is()) |
| { |
| xSeries->resetAllDataPoints(); |
| } |
| } |
| |
| // Get the properties of the Theme |
| // we may want to set it to the chart, or overwrite it with the chart element properties. |
| std::vector<std::pair<OUString, uno::Any>>& aProperties |
| = aTheme.m_aElements[static_cast<int>(nElementID)].m_aProperties; |
| |
| for (size_t i = 0; i < xProperties.size(); i++) |
| { |
| if (bSet) |
| { |
| // set properties of chart from the stored in theme |
| for (auto& aProperty : aProperties) |
| { |
| try |
| { |
| if (aProperty.second != xProperties[i]->getPropertyValue(aProperty.first)) |
| { |
| xProperties[i]->setPropertyValue(aProperty.first, aProperty.second); |
| } |
| } |
| catch (const beans::UnknownPropertyException&) |
| { |
| TOOLS_WARN_EXCEPTION("chart2", "unknown Property: " << aProperty.first); |
| } |
| catch (const uno::Exception&) |
| { |
| TOOLS_WARN_EXCEPTION("chart2", ""); |
| } |
| } |
| } |
| else |
| { |
| // get style from chart and save to theme |
| // TODO: we should save not all the properties |
| Reference<beans::XPropertySetInfo> xInfo = xProperties[0]->getPropertySetInfo(); |
| if (xInfo.is()) |
| { |
| auto aPropSeq = xInfo->getProperties(); |
| uno::Any aValue; |
| if (aPropSeq.getLength() > 0) |
| { |
| // clear the theme properties, and reserve enought for the current style |
| // TODO: we may want to keep some that cannot be overwritten? |
| aProperties.clear(); |
| aProperties.reserve(aPropSeq.getLength()); |
| |
| std::set<OUString>& rPropSet = lcl_GetPropertyNameVec(); |
| for (auto& aProp : aPropSeq) |
| { |
| std::set<OUString>::const_iterator aIt(rPropSet.find(aProp.Name)); |
| if (aIt != rPropSet.end()) |
| { |
| aValue = xProperties[0]->getPropertyValue(aProp.Name); |
| if (aValue.hasValue()) |
| { |
| aProperties.push_back(std::make_pair(aProp.Name, aValue)); |
| } |
| } |
| } |
| } |
| } |
| // if there is more element in xProperties, then it is a Title with multiple styles .. |
| // we can save only 1 so we are finished here |
| break; |
| } |
| } |
| } |
| |
| auto aChild = aHierarchy.getChildren(aElement); |
| if (aChild.size() > 0) |
| { |
| for (auto& aCElement : aChild) |
| { |
| lcl_SetOrGetThemeToOrFromElement(bSet, aTheme, aHierarchy, aCElement, xCModel); |
| } |
| } |
| } |
| |
| void ChartController::setTheme(sal_uInt32 nIndex) |
| { |
| if (nIndex >= ChartThemesType::getInstance().getThemesCount()) |
| return; |
| |
| ChartThemeType& aTheme = ChartThemesType::getInstance().m_aThemes[nIndex]; |
| |
| rtl::Reference<::chart::ChartModel> xCModel = getChartModel(); |
| |
| xCModel->lockControllers(); |
| ChartView* pExplicitValueProvider = nullptr; |
| ObjectHierarchy aHierarchy(xCModel, pExplicitValueProvider, true, true); |
| auto aTop = aHierarchy.getTopLevelChildren(); |
| for (auto& aElement : aTop) |
| { |
| lcl_SetOrGetThemeToOrFromElement(true, aTheme, aHierarchy, aElement, xCModel); |
| } |
| xCModel->unlockControllers(); |
| } |
| |
| void ChartController::saveTheme(sal_uInt32 nIndex) |
| { |
| ChartThemesType& aChartTypes = ChartThemesType::getInstance(); |
| if (nIndex >= aChartTypes.getThemesCount()) |
| { |
| nIndex = aChartTypes.getThemesCount(); |
| // maximum 256 chart styles |
| // Todo: maybe we could figure out some other way to limit this. |
| if (nIndex >= 256) |
| return; |
| aChartTypes.m_aThemes.resize(nIndex + 1); |
| } |
| |
| ChartThemeType& aTheme = aChartTypes.m_aThemes[nIndex]; |
| |
| rtl::Reference<::chart::ChartModel> xCModel = getChartModel(); |
| |
| ChartView* pExplicitValueProvider = nullptr; |
| ObjectHierarchy aHierarchy(xCModel, pExplicitValueProvider, true, true); |
| auto aTop = aHierarchy.getTopLevelChildren(); |
| for (auto& aElement : aTop) |
| { |
| lcl_SetOrGetThemeToOrFromElement(false, aTheme, aHierarchy, aElement, xCModel); |
| } |
| } |
| |
| ChartThemesType* ChartThemesType::m_aInstance = nullptr; |
| ChartThemesType& ChartThemesType::getInstance() |
| { |
| if (!m_aInstance) { |
| m_aInstance = new ChartThemesType; |
| } |
| return *m_aInstance; |
| } |
| |
| typedef std::pair<OUString, sal_uInt8> tPropertyNameWithMemberId; |
| typedef std::map<sal_uInt16, tPropertyNameWithMemberId> ItemPropertyMapType; |
| |
| static ItemPropertyMapType& lcl_GetPropertyMap() |
| { |
| static ItemPropertyMapType aCharacterPropertyMap{ |
| { EE_CHAR_COLOR, { "CharColor", 0 } }, |
| { EE_CHAR_KERNING, { "CharKerning", 0 } }, |
| { EE_CHAR_SHADOW, { "CharShadowed", 0 } }, |
| { EE_CHAR_RELIEF, { "CharRelief", 0 } }, |
| { EE_CHAR_OUTLINE, { "CharContoured", 0 } }, |
| { EE_CHAR_EMPHASISMARK, { "CharEmphasis", 0 } }, |
| }; |
| return aCharacterPropertyMap; |
| } |
| |
| void ChartElementThemeType::convertPoolItemsToProperties(std::vector<SfxPoolItem*> aPoolItems) |
| { |
| uno::Any aValue; |
| |
| for (SfxPoolItem* pPoolItem : aPoolItems) |
| { |
| sal_uInt16 nWhichId = pPoolItem->Which(); |
| |
| switch (nWhichId) |
| { |
| case EE_CHAR_FONTINFO: |
| case EE_CHAR_FONTINFO_CJK: |
| case EE_CHAR_FONTINFO_CTL: |
| { |
| OUString aPostfix; |
| if (nWhichId == EE_CHAR_FONTINFO_CJK) |
| aPostfix = "Asian"; |
| else if (nWhichId == EE_CHAR_FONTINFO_CTL) |
| aPostfix = "Complex"; |
| |
| const SvxFontItem& rItem = static_cast<const SvxFontItem&>(*pPoolItem); |
| |
| if (rItem.QueryValue(aValue, MID_FONT_FAMILY_NAME)) |
| { |
| m_aProperties.push_back( |
| std::pair<OUString, uno::Any>("CharFontName" + aPostfix, aValue)); |
| } |
| if (rItem.QueryValue(aValue, MID_FONT_FAMILY)) |
| { |
| m_aProperties.push_back( |
| std::pair<OUString, uno::Any>("CharFontFamily" + aPostfix, aValue)); |
| } |
| if (rItem.QueryValue(aValue, MID_FONT_STYLE_NAME)) |
| { |
| m_aProperties.push_back( |
| std::pair<OUString, uno::Any>("CharFontStyleName" + aPostfix, aValue)); |
| } |
| if (rItem.QueryValue(aValue, MID_FONT_CHAR_SET)) |
| { |
| m_aProperties.push_back( |
| std::pair<OUString, uno::Any>("CharFontCharSet" + aPostfix, aValue)); |
| } |
| if (rItem.QueryValue(aValue, MID_FONT_PITCH)) |
| { |
| m_aProperties.push_back( |
| std::pair<OUString, uno::Any>("CharFontPitch" + aPostfix, aValue)); |
| } |
| } |
| break; |
| |
| case EE_CHAR_UNDERLINE: |
| { |
| const SvxUnderlineItem& rItem = static_cast<const SvxUnderlineItem&>(*pPoolItem); |
| |
| if (rItem.QueryValue(aValue, MID_TL_STYLE)) |
| { |
| m_aProperties.push_back(std::make_pair(u"CharUnderline"_ustr, aValue)); |
| } |
| |
| if (rItem.QueryValue(aValue, MID_TL_COLOR)) |
| { |
| m_aProperties.push_back(std::make_pair(u"CharUnderlineColor"_ustr, aValue)); |
| } |
| |
| if (rItem.QueryValue(aValue, MID_TL_HASCOLOR)) |
| { |
| m_aProperties.push_back(std::make_pair(u"CharUnderlineHasColor"_ustr, aValue)); |
| } |
| } |
| break; |
| |
| case EE_CHAR_STRIKEOUT: |
| { |
| const SvxCrossedOutItem& rItem = static_cast<const SvxCrossedOutItem&>(*pPoolItem); |
| |
| if (rItem.QueryValue(aValue, MID_TL_STYLE)) |
| { |
| m_aProperties.push_back(std::make_pair(u"CharStrikeout"_ustr, aValue)); |
| } |
| } |
| break; |
| |
| case EE_CHAR_OVERLINE: |
| { |
| const SvxOverlineItem& rItem = static_cast<const SvxOverlineItem&>(*pPoolItem); |
| |
| if (rItem.QueryValue(aValue, MID_TL_STYLE)) |
| { |
| m_aProperties.push_back(std::make_pair(u"CharOverline"_ustr, aValue)); |
| } |
| |
| if (rItem.QueryValue(aValue, MID_TL_COLOR)) |
| { |
| m_aProperties.push_back(std::make_pair(u"CharOverlineColor"_ustr, aValue)); |
| } |
| |
| if (rItem.QueryValue(aValue, MID_TL_HASCOLOR)) |
| { |
| m_aProperties.push_back(std::make_pair(u"CharOverlineHasColor"_ustr, aValue)); |
| } |
| } |
| break; |
| |
| case EE_CHAR_ITALIC: |
| case EE_CHAR_ITALIC_CJK: |
| case EE_CHAR_ITALIC_CTL: |
| { |
| OUString aPostfix; |
| if (nWhichId == EE_CHAR_ITALIC_CJK) |
| aPostfix = "Asian"; |
| else if (nWhichId == EE_CHAR_ITALIC_CTL) |
| aPostfix = "Complex"; |
| |
| const SvxPostureItem& rItem = static_cast<const SvxPostureItem&>(*pPoolItem); |
| |
| if (rItem.QueryValue(aValue, MID_POSTURE)) |
| { |
| m_aProperties.push_back( |
| std::pair<OUString, uno::Any>("CharPosture" + aPostfix, aValue)); |
| } |
| } |
| break; |
| |
| case EE_CHAR_WEIGHT: |
| case EE_CHAR_WEIGHT_CJK: |
| case EE_CHAR_WEIGHT_CTL: |
| { |
| OUString aPostfix; |
| if (nWhichId == EE_CHAR_WEIGHT_CJK) |
| aPostfix = "Asian"; |
| else if (nWhichId == EE_CHAR_WEIGHT_CTL) |
| aPostfix = "Complex"; |
| |
| const SvxWeightItem& rItem = static_cast<const SvxWeightItem&>(*pPoolItem); |
| |
| if (rItem.QueryValue(aValue, MID_WEIGHT)) |
| { |
| m_aProperties.push_back( |
| std::pair<OUString, uno::Any>("CharWeight" + aPostfix, aValue)); |
| } |
| } |
| break; |
| |
| case EE_CHAR_FONTHEIGHT: |
| case EE_CHAR_FONTHEIGHT_CJK: |
| case EE_CHAR_FONTHEIGHT_CTL: |
| { |
| OUString aPostfix; |
| if (nWhichId == EE_CHAR_FONTHEIGHT_CJK) |
| aPostfix = "Asian"; |
| else if (nWhichId == EE_CHAR_FONTHEIGHT_CTL) |
| aPostfix = "Complex"; |
| |
| const SvxFontHeightItem& rItem = static_cast<const SvxFontHeightItem&>(*pPoolItem); |
| |
| if (rItem.QueryValue(aValue, MID_FONTHEIGHT)) |
| { |
| m_aProperties.push_back( |
| std::pair<OUString, uno::Any>("CharHeight" + aPostfix, aValue)); |
| } |
| } |
| break; |
| default: |
| { |
| ItemPropertyMapType& rMap(lcl_GetPropertyMap()); |
| ItemPropertyMapType::const_iterator aIt(rMap.find(nWhichId)); |
| |
| if (aIt != rMap.end()) |
| { |
| tPropertyNameWithMemberId aProperty = (*aIt).second; |
| |
| pPoolItem->QueryValue(aValue, aProperty.second /* nMemberId */); |
| m_aProperties.push_back(std::make_pair(aProperty.first, aValue)); |
| } |
| } |
| break; |
| } |
| } |
| } |
| |
| ChartThemesType::ChartThemesType() |
| { |
| // TODO: find a better place/way to store the pre-defined chart themes |
| // Now it is just some placeholder test cases. |
| // by default there is now 4 pre-defined chart style |
| // and 7 chart elements |
| size_t ElementCount = ChartThemeType::ElementCount; |
| m_aThemes.resize(FixedCount); |
| for (size_t i = 0; i < FixedCount * ElementCount; i++) |
| { |
| std::vector<SfxPoolItem*> aSet; |
| |
| ChartThemeElementID nElementType = ChartThemeElementID(i % ElementCount); |
| |
| sal_uInt32 nHeight = 240 + i * 10; |
| sal_uInt32 nColor = 0xff000000 + (170 << (i % 15)); |
| const Color aColor(ColorAlpha, nColor); |
| short nKerning = 0; |
| FontWeight nBold = WEIGHT_NORMAL; |
| FontItalic nItalic = ITALIC_NONE; |
| |
| if (nElementType == ChartThemeElementID::Title) |
| { |
| nHeight *= 2; |
| nKerning = 100 * (i / ElementCount + 1); |
| nBold = WEIGHT_BOLD; |
| } |
| if (nElementType == ChartThemeElementID::SubTitle) |
| { |
| nHeight = nHeight * 3 / 2; |
| nKerning = -50 * (i / ElementCount + 1); |
| nItalic = ITALIC_NORMAL; |
| } |
| |
| aSet.push_back(new SvxFontItem(FAMILY_DONTKNOW, u"Kristen ITC"_ustr, u""_ustr, |
| PITCH_VARIABLE, |
| RTL_TEXTENCODING_DONTKNOW, EE_CHAR_FONTINFO)); |
| aSet.push_back(new SvxFontItem(FAMILY_DONTKNOW, u"Mistral"_ustr, u""_ustr, PITCH_VARIABLE, |
| RTL_TEXTENCODING_DONTKNOW, EE_CHAR_FONTINFO_CJK)); |
| aSet.push_back(new SvxFontItem(FAMILY_DONTKNOW, u"Maiandra GD"_ustr, u""_ustr, |
| PITCH_VARIABLE, |
| RTL_TEXTENCODING_DONTKNOW, EE_CHAR_FONTINFO_CTL)); |
| |
| aSet.push_back(new SvxFontHeightItem(nHeight, 100, EE_CHAR_FONTHEIGHT)); |
| aSet.push_back(new SvxFontHeightItem(nHeight + 1, 100, EE_CHAR_FONTHEIGHT_CJK)); |
| aSet.push_back(new SvxFontHeightItem(nHeight + 2, 100, EE_CHAR_FONTHEIGHT_CTL)); |
| |
| aSet.push_back(new SvxWeightItem(nBold, EE_CHAR_WEIGHT)); |
| aSet.push_back(new SvxWeightItem(nBold, EE_CHAR_WEIGHT_CJK)); |
| aSet.push_back(new SvxWeightItem(nBold, EE_CHAR_WEIGHT_CTL)); |
| |
| aSet.push_back(new SvxPostureItem(nItalic, EE_CHAR_ITALIC)); |
| aSet.push_back(new SvxPostureItem(nItalic, EE_CHAR_ITALIC_CJK)); |
| aSet.push_back(new SvxPostureItem(nItalic, EE_CHAR_ITALIC_CTL)); |
| |
| aSet.push_back(new SvxUnderlineItem(LINESTYLE_LONGDASH, EE_CHAR_UNDERLINE)); |
| |
| aSet.push_back(new SvxCrossedOutItem(STRIKEOUT_BOLD, EE_CHAR_STRIKEOUT)); |
| aSet.push_back(new SvxColorItem(aColor, EE_CHAR_COLOR)); |
| aSet.push_back(new SvxShadowedItem(true, EE_CHAR_SHADOW)); |
| if (nKerning != 0) |
| { |
| aSet.push_back(new SvxKerningItem(nKerning, EE_CHAR_KERNING)); |
| } |
| //convert them to properties |
| m_aThemes[i / ElementCount].m_aElements[i % ElementCount].convertPoolItemsToProperties( |
| aSet); |
| } |
| } |
| |
| } //namespace chart |
| |
| /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |