/* -*- 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 .
*/
// 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/fontitem.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
{
usingnamespace ::com::sun::star; usingnamespace ::com::sun::star::accessibility; usingnamespace ::com::sun::star::chart2; using ::com::sun::star::uno::Reference; using ::com::sun::star::uno::Sequence;
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
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" );
}
}
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;
}
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( 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 );
// 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();
} elseif( 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;
//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? returnfalse; //behave passive if already disposed or suspended
aGuard.clear();
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, impl_getAvailableCommands() );
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() ) ) );
uno::Reference< frame::XFrame > SAL_CALL ChartController::getFrame()
{ //provides access to owner frame of this controller //return the frame containing this controller
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();
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() ) returnfalse; //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" ); returntrue;
}
css::uno::Reference<css::awt::XWindow> SAL_CALL ChartController::getComponentWindow()
{ // it is a special characteristic of ChartController // that it simultaneously provides the XWindow functionality returnthis;
}
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" );
//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();
}
// 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.
//--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
}
}
}
// 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();
}
});
}
// ____ 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 ?
}
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; returndynamic_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);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.