436 lines
13 KiB
C++
436 lines
13 KiB
C++
/*************************************************************************
|
|
*
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* Copyright 2000, 2010 Oracle and/or its affiliates.
|
|
*
|
|
* OpenOffice.org - a multi-platform office productivity suite
|
|
*
|
|
* This file is part of OpenOffice.org.
|
|
*
|
|
* OpenOffice.org is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License version 3
|
|
* only, as published by the Free Software Foundation.
|
|
*
|
|
* OpenOffice.org is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License version 3 for more details
|
|
* (a copy is included in the LICENSE file that accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* version 3 along with OpenOffice.org. If not, see
|
|
* <http://www.openoffice.org/license.html>
|
|
* for a copy of the LGPLv3 License.
|
|
*
|
|
************************************************************************/
|
|
|
|
// MARKER(update_precomp.py): autogen include statement, do not remove
|
|
#include "precompiled_chart2.hxx"
|
|
|
|
#include "UndoManager.hxx"
|
|
#include "ImplUndoManager.hxx"
|
|
#include "DisposeHelper.hxx"
|
|
#include "MutexContainer.hxx"
|
|
#include "macros.hxx"
|
|
#include "ChartViewHelper.hxx"
|
|
|
|
#include <com/sun/star/util/XCloneable.hpp>
|
|
#include <com/sun/star/frame/XModel.hpp>
|
|
#include <com/sun/star/chart2/XChartDocument.hpp>
|
|
|
|
#include <unotools/configitem.hxx>
|
|
#include <cppuhelper/compbase1.hxx>
|
|
#include <rtl/uuid.h>
|
|
#include <svx/svdundo.hxx>
|
|
|
|
#include <functional>
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
using ::com::sun::star::uno::Reference;
|
|
using ::com::sun::star::uno::Sequence;
|
|
using ::rtl::OUString;
|
|
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
namespace chart
|
|
{
|
|
|
|
namespace impl
|
|
{
|
|
typedef ::cppu::WeakComponentImplHelper1<
|
|
util::XModifyBroadcaster >
|
|
ModifyBroadcaster_Base;
|
|
|
|
class ModifyBroadcaster :
|
|
public ::chart::MutexContainer,
|
|
public ModifyBroadcaster_Base
|
|
{
|
|
public:
|
|
ModifyBroadcaster();
|
|
|
|
void fireEvent();
|
|
|
|
protected:
|
|
// ____ XModifyBroadcaster ____
|
|
virtual void SAL_CALL addModifyListener( const Reference< util::XModifyListener >& xListener )
|
|
throw (uno::RuntimeException);
|
|
virtual void SAL_CALL removeModifyListener( const Reference< util::XModifyListener >& xListener )
|
|
throw (uno::RuntimeException);
|
|
};
|
|
|
|
ModifyBroadcaster::ModifyBroadcaster() :
|
|
ModifyBroadcaster_Base( m_aMutex )
|
|
{}
|
|
|
|
void SAL_CALL ModifyBroadcaster::addModifyListener(
|
|
const Reference< util::XModifyListener >& xListener )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
rBHelper.addListener( ::getCppuType( & xListener ), xListener);
|
|
}
|
|
|
|
void SAL_CALL ModifyBroadcaster::removeModifyListener(
|
|
const Reference< util::XModifyListener >& xListener )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
rBHelper.removeListener( ::getCppuType( & xListener ), xListener );
|
|
}
|
|
|
|
void ModifyBroadcaster::fireEvent()
|
|
{
|
|
::cppu::OInterfaceContainerHelper* pIC = rBHelper.getContainer(
|
|
::getCppuType((const uno::Reference< util::XModifyListener >*)0) );
|
|
if( pIC )
|
|
{
|
|
lang::EventObject aEvent( static_cast< lang::XComponent* >( this ) );
|
|
::cppu::OInterfaceIteratorHelper aIt( *pIC );
|
|
while( aIt.hasMoreElements() )
|
|
(static_cast< util::XModifyListener*>(aIt.next()))->modified( aEvent );
|
|
}
|
|
}
|
|
|
|
} // namespace impl
|
|
|
|
UndoManager::UndoManager() :
|
|
impl::UndoManager_Base( m_aMutex ),
|
|
m_apUndoStack( new impl::UndoStack()),
|
|
m_apRedoStack( new impl::UndoStack()),
|
|
m_pLastRemeberedUndoElement( 0 ),
|
|
m_nMaxNumberOfUndos( 100 ),
|
|
m_pModifyBroadcaster( 0 )
|
|
{}
|
|
|
|
UndoManager::~UndoManager()
|
|
{
|
|
DisposeHelper::Dispose( m_xModifyBroadcaster );
|
|
m_apUndoStack->disposeAndClear();
|
|
m_apRedoStack->disposeAndClear();
|
|
|
|
delete m_pLastRemeberedUndoElement;
|
|
m_pLastRemeberedUndoElement = 0;
|
|
}
|
|
|
|
void UndoManager::addShapeUndoAction( SdrUndoAction* pAction )
|
|
{
|
|
if ( !pAction )
|
|
{
|
|
return;
|
|
}
|
|
|
|
impl::ShapeUndoElement* pShapeUndoElement = new impl::ShapeUndoElement( pAction->GetComment(), pAction );
|
|
if ( pShapeUndoElement )
|
|
{
|
|
m_apUndoStack->push( pShapeUndoElement );
|
|
m_apRedoStack->disposeAndClear();
|
|
if ( !m_apUndoStepsConfigItem.get() )
|
|
{
|
|
retrieveConfigUndoSteps();
|
|
}
|
|
fireModifyEvent();
|
|
}
|
|
}
|
|
|
|
void UndoManager::impl_undoRedo(
|
|
Reference< frame::XModel > & xCurrentModel,
|
|
impl::UndoStack * pStackToRemoveFrom,
|
|
impl::UndoStack * pStackToAddTo,
|
|
bool bUndo )
|
|
{
|
|
if( pStackToRemoveFrom && ! pStackToRemoveFrom->empty() )
|
|
{
|
|
// get model from undo/redo
|
|
impl::UndoElement * pTop( pStackToRemoveFrom->top());
|
|
if( pTop )
|
|
{
|
|
impl::ShapeUndoElement* pShapeUndoElement = dynamic_cast< impl::ShapeUndoElement* >( pTop );
|
|
if ( pShapeUndoElement )
|
|
{
|
|
impl::ShapeUndoElement* pNewShapeUndoElement = new impl::ShapeUndoElement( *pShapeUndoElement );
|
|
pStackToAddTo->push( pNewShapeUndoElement );
|
|
SdrUndoAction* pAction = pNewShapeUndoElement->getSdrUndoAction();
|
|
if ( pAction )
|
|
{
|
|
if ( bUndo )
|
|
{
|
|
pAction->Undo();
|
|
}
|
|
else
|
|
{
|
|
pAction->Redo();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// put a clone of current model into redo/undo stack with the same
|
|
// action string as the undo/redo
|
|
pStackToAddTo->push( pTop->createFromModel( xCurrentModel ));
|
|
// change current model by properties of the model from undo
|
|
pTop->applyToModel( xCurrentModel );
|
|
}
|
|
// remove the top undo element
|
|
pStackToRemoveFrom->pop(), pTop = 0;
|
|
ChartViewHelper::setViewToDirtyState( xCurrentModel );
|
|
fireModifyEvent();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OSL_ENSURE( false, "Can't Undo/Redo" );
|
|
}
|
|
}
|
|
|
|
void UndoManager::fireModifyEvent()
|
|
{
|
|
if( m_xModifyBroadcaster.is())
|
|
m_pModifyBroadcaster->fireEvent();
|
|
}
|
|
|
|
|
|
// ____ ConfigItemListener ____
|
|
void UndoManager::notify( const ::rtl::OUString & rPropertyName )
|
|
{
|
|
OSL_ENSURE( rPropertyName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Steps" )),
|
|
"Unwanted config property change Notified" );
|
|
if( rPropertyName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Steps" )))
|
|
retrieveConfigUndoSteps();
|
|
}
|
|
|
|
void UndoManager::retrieveConfigUndoSteps()
|
|
{
|
|
if( ! m_apUndoStepsConfigItem.get())
|
|
m_apUndoStepsConfigItem.reset( new impl::UndoStepsConfigItem( *this ));
|
|
m_nMaxNumberOfUndos = m_apUndoStepsConfigItem->getUndoSteps();
|
|
m_apUndoStack->limitSize( m_nMaxNumberOfUndos );
|
|
m_apRedoStack->limitSize( m_nMaxNumberOfUndos );
|
|
|
|
// a list of available undo steps could shrink here
|
|
fireModifyEvent();
|
|
}
|
|
|
|
// ____ XModifyBroadcaster ____
|
|
void SAL_CALL UndoManager::addModifyListener( const Reference< util::XModifyListener >& aListener )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
if( ! m_xModifyBroadcaster.is())
|
|
{
|
|
m_pModifyBroadcaster = new impl::ModifyBroadcaster();
|
|
m_xModifyBroadcaster.set( static_cast< cppu::OWeakObject* >( m_pModifyBroadcaster ), uno::UNO_QUERY );
|
|
}
|
|
m_xModifyBroadcaster->addModifyListener( aListener );
|
|
}
|
|
|
|
void SAL_CALL UndoManager::removeModifyListener( const Reference< util::XModifyListener >& aListener )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
if( ! m_xModifyBroadcaster.is())
|
|
{
|
|
m_pModifyBroadcaster = new impl::ModifyBroadcaster();
|
|
m_xModifyBroadcaster.set( static_cast< cppu::OWeakObject* >( m_pModifyBroadcaster ), uno::UNO_QUERY );
|
|
}
|
|
m_xModifyBroadcaster->removeModifyListener( aListener );
|
|
}
|
|
|
|
// ____ chart2::XUndoManager ____
|
|
void SAL_CALL UndoManager::preAction( const Reference< frame::XModel >& xModelBeforeChange )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
OSL_ENSURE( ! m_pLastRemeberedUndoElement, "Looks like postAction or cancelAction call was missing" );
|
|
m_pLastRemeberedUndoElement = new impl::UndoElement( xModelBeforeChange );
|
|
}
|
|
|
|
void SAL_CALL UndoManager::preActionWithArguments(
|
|
const Reference< frame::XModel >& xModelBeforeChange,
|
|
const Sequence< beans::PropertyValue >& aArguments )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
bool bActionHandled( false );
|
|
OSL_ENSURE( ! m_pLastRemeberedUndoElement, "Looks like postAction or cancelAction call was missing" );
|
|
if( aArguments.getLength() > 0 )
|
|
{
|
|
OSL_ENSURE( aArguments.getLength() == 1, "More than one argument is not supported yet" );
|
|
if( aArguments[0].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("WithData")))
|
|
{
|
|
m_pLastRemeberedUndoElement = new impl::UndoElementWithData( xModelBeforeChange );
|
|
bActionHandled = true;
|
|
}
|
|
else if( aArguments[0].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("WithSelection")))
|
|
{
|
|
m_pLastRemeberedUndoElement = new impl::UndoElementWithSelection( xModelBeforeChange );
|
|
bActionHandled = true;
|
|
}
|
|
}
|
|
|
|
if( !bActionHandled )
|
|
preAction( xModelBeforeChange );
|
|
}
|
|
|
|
void SAL_CALL UndoManager::postAction( const OUString& aUndoText )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
OSL_ENSURE( m_pLastRemeberedUndoElement, "Looks like preAction call was missing" );
|
|
if( m_pLastRemeberedUndoElement )
|
|
{
|
|
m_pLastRemeberedUndoElement->setActionString( aUndoText );
|
|
m_apUndoStack->push( m_pLastRemeberedUndoElement );
|
|
m_pLastRemeberedUndoElement = 0;
|
|
|
|
// redo no longer possible
|
|
m_apRedoStack->disposeAndClear();
|
|
|
|
// it suffices to get the number of undo steps from config after the
|
|
// first time postAction has been called
|
|
if( ! m_apUndoStepsConfigItem.get())
|
|
retrieveConfigUndoSteps();
|
|
|
|
fireModifyEvent();
|
|
}
|
|
}
|
|
|
|
void SAL_CALL UndoManager::cancelAction()
|
|
throw (uno::RuntimeException)
|
|
{
|
|
delete m_pLastRemeberedUndoElement;
|
|
m_pLastRemeberedUndoElement = 0;
|
|
}
|
|
|
|
void SAL_CALL UndoManager::cancelActionWithUndo( Reference< frame::XModel >& xModelToRestore )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
if( m_pLastRemeberedUndoElement )
|
|
{
|
|
m_pLastRemeberedUndoElement->applyToModel( xModelToRestore );
|
|
cancelAction();
|
|
}
|
|
}
|
|
|
|
void SAL_CALL UndoManager::undo( Reference< frame::XModel >& xCurrentModel )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
OSL_ASSERT( m_apUndoStack.get() && m_apRedoStack.get());
|
|
impl_undoRedo( xCurrentModel, m_apUndoStack.get(), m_apRedoStack.get(), true );
|
|
}
|
|
|
|
void SAL_CALL UndoManager::redo( Reference< frame::XModel >& xCurrentModel )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
OSL_ASSERT( m_apUndoStack.get() && m_apRedoStack.get());
|
|
impl_undoRedo( xCurrentModel, m_apRedoStack.get(), m_apUndoStack.get(), false );
|
|
}
|
|
|
|
::sal_Bool SAL_CALL UndoManager::undoPossible()
|
|
throw (uno::RuntimeException)
|
|
{
|
|
return ! m_apUndoStack->empty();
|
|
}
|
|
|
|
::sal_Bool SAL_CALL UndoManager::redoPossible()
|
|
throw (uno::RuntimeException)
|
|
{
|
|
return ! m_apRedoStack->empty();
|
|
}
|
|
|
|
OUString SAL_CALL UndoManager::getCurrentUndoString()
|
|
throw (uno::RuntimeException)
|
|
{
|
|
return m_apUndoStack->topUndoString();
|
|
}
|
|
|
|
OUString SAL_CALL UndoManager::getCurrentRedoString()
|
|
throw (uno::RuntimeException)
|
|
{
|
|
return m_apRedoStack->topUndoString();
|
|
}
|
|
|
|
Sequence< OUString > SAL_CALL UndoManager::getAllUndoStrings()
|
|
throw (uno::RuntimeException)
|
|
{
|
|
return m_apUndoStack->getUndoStrings();
|
|
}
|
|
|
|
Sequence< OUString > SAL_CALL UndoManager::getAllRedoStrings()
|
|
throw (uno::RuntimeException)
|
|
{
|
|
return m_apRedoStack->getUndoStrings();
|
|
}
|
|
|
|
// ____ XUndoHelper ____
|
|
Reference< frame::XModel > SAL_CALL UndoManager::getModelCloneForUndo(
|
|
const Reference< frame::XModel >& xModelBeforeChange )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
return impl::UndoElement::cloneModel( xModelBeforeChange );
|
|
}
|
|
|
|
void SAL_CALL UndoManager::applyModelContent(
|
|
Reference< frame::XModel >& xModelToChange,
|
|
const Reference< frame::XModel >& xModelToCopyFrom )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
impl::UndoElement::applyModelContentToModel( xModelToChange, xModelToCopyFrom );
|
|
}
|
|
|
|
// ____ XUnoTunnel ____
|
|
sal_Int64 UndoManager::getSomething( const Sequence< sal_Int8 >& rId )
|
|
throw (uno::RuntimeException)
|
|
{
|
|
if ( rId.getLength() == 16 && 0 == rtl_compareMemory( getUnoTunnelId().getConstArray(), rId.getConstArray(), 16 ) )
|
|
{
|
|
return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this ) );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// static
|
|
const Sequence< sal_Int8 >& UndoManager::getUnoTunnelId()
|
|
{
|
|
static Sequence< sal_Int8 >* pSeq = 0;
|
|
if( !pSeq )
|
|
{
|
|
osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );
|
|
if( !pSeq )
|
|
{
|
|
static Sequence< sal_Int8 > aSeq( 16 );
|
|
rtl_createUuid( (sal_uInt8*)aSeq.getArray(), 0, sal_True );
|
|
pSeq = &aSeq;
|
|
}
|
|
}
|
|
return *pSeq;
|
|
}
|
|
|
|
// static
|
|
UndoManager* UndoManager::getImplementation( const Reference< uno::XInterface > xObj )
|
|
{
|
|
UndoManager* pRet = NULL;
|
|
Reference< lang::XUnoTunnel > xUT( xObj, uno::UNO_QUERY );
|
|
if ( xUT.is() )
|
|
{
|
|
pRet = reinterpret_cast< UndoManager* >( sal::static_int_cast< sal_IntPtr >( xUT->getSomething( getUnoTunnelId() ) ) );
|
|
}
|
|
return pRet;
|
|
}
|
|
|
|
} // namespace chart
|