/* -*- 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 .
*/
void SfxUndoAction::Repeat(SfxRepeatTarget&)
{ // These are only conceptually pure virtual
assert(!"pure virtual function called: SfxUndoAction::Repeat()");
}
The Undo action will be put into a list, whose members will be deleted from within the destructor of the UndoManagerGuard. This deletion will happen without the UndoManager's mutex locked.
*/ void markForDeletion( std::unique_ptr<SfxUndoAction> i_action )
{ // remember
assert ( i_action );
m_aUndoActionsCleanup.emplace_back( std::move(i_action) );
}
/** schedules the given SfxUndoListener method to be called for all registered listeners.
The notification will happen after the Undo manager's mutex has been released, and after all pending deletions of Undo actions are done.
*/ void scheduleNotification( UndoListenerVoidMethod i_notificationMethod )
{
m_notifiers.emplace_back( i_notificationMethod );
}
// Remove entries from the pActUndoArray when we have to reduce // the number of entries due to a lower nMaxUndoActionCount. // Both redo and undo action entries will be removed until we reached the // new nMaxUndoActionCount.
SAL_WARN_IF( ImplIsInListAction_Lock(), "svl", "SfxUndoManager::Clear: suspicious call - do you really wish to clear the current level?" );
ImplClearCurrentLevel_NoNotify( aGuard );
void SfxUndoManager::ImplClearRedo_NoLock( boolconst i_currentLevel )
{ if (IsDoing())
{ // cannot clear redo while undo/redo is in process. Delay ClearRedo until safe to clear. // (assuming if TopLevel requests a clear, it should have priority over CurrentLevel) if (!m_xData->moNeedsClearRedo.has_value() || i_currentLevel == TopLevel)
m_xData->moNeedsClearRedo = i_currentLevel; return;
}
UndoManagerGuard aGuard( *m_xData );
ImplClearRedo( aGuard, i_currentLevel );
}
void SfxUndoManager::ClearRedo()
{
SAL_WARN_IF( IsInListAction(), "svl", "SfxUndoManager::ClearRedo: suspicious call - do you really wish to clear the current level?" );
ImplClearRedo_NoLock( CurrentLevel );
}
ImplCheckEmptyActions(); // notification - only if the top level's stack was cleared if ( i_currentLevel == SfxUndoManager::TopLevel )
i_guard.scheduleNotification( &SfxUndoListener::clearedRedo );
}
if ( ImplIsInListAction_Lock() )
{
assert(!"SfxUndoManager::Undo: not possible when within a list action!"); returnfalse;
}
if ( m_xData->pActUndoArray->nCurUndoAction == 0 )
{
SAL_WARN("svl", "SfxUndoManager::Undo: undo stack is empty!" ); returnfalse;
}
if (i_contextOrNull && i_contextOrNull->GetUndoOffset() > 0)
{
size_t nCurrent = m_xData->pActUndoArray->nCurUndoAction;
size_t nOffset = i_contextOrNull->GetUndoOffset(); if (nCurrent >= nOffset + 1)
{ // Move the action we want to execute to the top of the undo stack. // data() + nCurrent - nOffset - 1 is the start, data() + nCurrent - nOffset is what we // want to move to the top, maUndoActions.data() + nCurrent is past the end/top of the // undo stack.
std::rotate(m_xData->pActUndoArray->maUndoActions.data() + nCurrent - nOffset - 1,
m_xData->pActUndoArray->maUndoActions.data() + nCurrent - nOffset,
m_xData->pActUndoArray->maUndoActions.data() + nCurrent);
}
}
SfxUndoAction* pAction = m_xData->pActUndoArray->maUndoActions[ --m_xData->pActUndoArray->nCurUndoAction ].pAction.get(); const OUString sActionComment = pAction->GetComment(); try
{ // clear the guard/mutex before calling into the SfxUndoAction - this can be an extension-implemented UNO component // nowadays ... auto aResetGuard(aGuard.clear()); if ( i_contextOrNull != nullptr )
pAction->UndoWithContext( *i_contextOrNull ); else
pAction->Undo();
} catch( ... )
{ // in theory, somebody might have tampered with all of *m_xData while the mutex was unlocked. So, see if // we still find pAction in our current Undo array
size_t nCurAction = 0; while ( nCurAction < m_xData->pActUndoArray->maUndoActions.size() )
{ if ( m_xData->pActUndoArray->maUndoActions[ nCurAction++ ].pAction.get() == pAction )
{ // the Undo action is still there ... // assume the error is a permanent failure, and clear the Undo stack
ImplClearUndo( aGuard ); throw;
}
}
SAL_WARN("svl", "SfxUndoManager::Undo: can't clear the Undo stack after the failure - some other party was faster ..." ); throw;
}
m_xData->mbDoing = false; if (m_xData->moNeedsClearRedo.has_value())
{
ImplClearRedo_NoLock(*m_xData->moNeedsClearRedo);
m_xData->moNeedsClearRedo.reset();
}
if ( ImplIsInListAction_Lock() )
{
assert(!"SfxUndoManager::Redo: not possible when within a list action!"); returnfalse;
}
if ( m_xData->pActUndoArray->nCurUndoAction >= m_xData->pActUndoArray->maUndoActions.size() )
{
SAL_WARN("svl", "SfxUndoManager::Redo: redo stack is empty!"); returnfalse;
}
SfxUndoAction* pAction = m_xData->pActUndoArray->maUndoActions[ m_xData->pActUndoArray->nCurUndoAction++ ].pAction.get(); const OUString sActionComment = pAction->GetComment(); try
{ // clear the guard/mutex before calling into the SfxUndoAction - this can be an extension-implemented UNO component // nowadays ... auto aResetGuard(aGuard.clear()); if ( i_contextOrNull != nullptr )
pAction->RedoWithContext( *i_contextOrNull ); else
pAction->Redo();
} catch( ... )
{ // in theory, somebody might have tampered with all of *m_xData while the mutex was unlocked. So, see if // we still find pAction in our current Undo array
size_t nCurAction = 0; while ( nCurAction < m_xData->pActUndoArray->maUndoActions.size() )
{ if ( m_xData->pActUndoArray->maUndoActions[ nCurAction ].pAction.get() == pAction )
{ // the Undo action is still there ... // assume the error is a permanent failure, and clear the Undo stack
ImplClearRedo( aGuard, SfxUndoManager::CurrentLevel ); throw;
}
++nCurAction;
}
SAL_WARN("svl", "SfxUndoManager::Redo: can't clear the Undo stack after the failure - some other party was faster ..." ); throw;
}
m_xData->mbDoing = false;
assert(!m_xData->moNeedsClearRedo.has_value() && "Assuming I don't need to handle it here. What about if thrown?");
ImplCheckEmptyActions();
aGuard.scheduleNotification( &SfxUndoListener::actionRedone, sActionComment );
/** * Inserts a ListUndoAction and sets its UndoArray as current.
*/ void SfxUndoManager::EnterListAction( const OUString& rComment, const OUString &rRepeatComment, sal_uInt16 nId,
ViewShellId nViewShellId )
{
UndoManagerGuard aGuard( *m_xData );
if( !ImplIsUndoEnabled_Lock() ) return;
if ( !m_xData->maUndoArray.nMaxUndoActions ) return;
SfxListUndoAction* pAction = new SfxListUndoAction( rComment, rRepeatComment, nId, nViewShellId, m_xData->pActUndoArray );
OSL_VERIFY( ImplAddUndoAction_NoNotify( std::unique_ptr<SfxUndoAction>(pAction), false, false, aGuard ) ); // expected to succeed: all conditions under which it could fail should have been checked already
m_xData->pActUndoArray = pAction;
if ( !m_xData->maUndoArray.nMaxUndoActions ) return 0;
if( !ImplIsInListAction_Lock() )
{
SAL_WARN("svl", "svl::SfxUndoManager::ImplLeaveListAction, called without calling EnterListAction()!" ); return 0;
}
assert(m_xData->pActUndoArray->pFatherUndoArray);
// the array/level which we're about to leave
SfxUndoArray* pArrayToLeave = m_xData->pActUndoArray; // one step up
m_xData->pActUndoArray = m_xData->pActUndoArray->pFatherUndoArray;
// If no undo actions were added to the list, delete the list action const size_t nListActionElements = pArrayToLeave->nCurUndoAction; if ( nListActionElements == 0 )
{
i_guard.markForDeletion( m_xData->pActUndoArray->Remove( --m_xData->pActUndoArray->nCurUndoAction ) );
i_guard.scheduleNotification( &SfxUndoListener::listActionCancelled ); return 0;
}
// now that it is finally clear the list action is non-trivial, and does participate in the Undo stack, clear // the redo stack
ImplClearRedo( i_guard, SfxUndoManager::CurrentLevel );
SfxUndoAction* pCurrentAction= m_xData->pActUndoArray->maUndoActions[ m_xData->pActUndoArray->nCurUndoAction-1 ].pAction.get();
SfxListUndoAction* pListAction = dynamic_cast< SfxListUndoAction * >( pCurrentAction );
ENSURE_OR_RETURN( pListAction, "SfxUndoManager::ImplLeaveListAction: list action expected at this position!", nListActionElements );
if ( i_merge )
{ // merge the list action with its predecessor on the same level
SAL_WARN_IF( m_xData->pActUndoArray->nCurUndoAction <= 1, "svl", "SfxUndoManager::ImplLeaveListAction: cannot merge the list action if there's no other action on the same level - check this beforehand!" ); if ( m_xData->pActUndoArray->nCurUndoAction > 1 )
{
std::unique_ptr<SfxUndoAction> pPreviousAction = m_xData->pActUndoArray->Remove( m_xData->pActUndoArray->nCurUndoAction - 2 );
--m_xData->pActUndoArray->nCurUndoAction;
pListAction->SetComment( pPreviousAction->GetComment() );
pListAction->Insert( std::move(pPreviousAction), 0 );
++pListAction->nCurUndoAction;
}
}
// if the undo array has no comment, try to get it from its children if ( pListAction->GetComment().isEmpty() )
{ for( size_t n = 0; n < pListAction->maUndoActions.size(); n++ )
{ if (!pListAction->maUndoActions[n].pAction->GetComment().isEmpty())
{
pListAction->SetComment( pListAction->maUndoActions[n].pAction->GetComment() ); break;
}
}
}
if ((m_xData->mnEmptyMark < i_mark) || (MARK_INVALID == i_mark))
{ return std::numeric_limits<size_t>::max(); // nothing to remove
} elseif (i_mark == m_xData->mnEmptyMark)
{
--m_xData->mnEmptyMark; // never returned from MarkTop => invalid return std::numeric_limits<size_t>::max();
}
for ( size_t i=0; i<m_xData->maUndoArray.maUndoActions.size(); ++i )
{
MarkedUndoAction& rAction = m_xData->maUndoArray.maUndoActions[i]; auto markPos = std::find(rAction.aMarks.begin(), rAction.aMarks.end(), i_mark); if (markPos != rAction.aMarks.end())
{
rAction.aMarks.erase( markPos ); return i;
}
}
SAL_WARN("svl", "SfxUndoManager::RemoveMark: mark not found!"); // TODO: this might be too offensive. There are situations where we implicitly remove marks // without our clients, in particular the client which created the mark, having a chance to know // about this.
void SfxUndoManager::UndoMark(UndoStackMark i_mark)
{
SfxMarkedUndoContext context(*this, i_mark); // Removes the mark if (context.GetUndoOffset() == std::numeric_limits<size_t>::max()) return; // nothing to undo
if ( IsInListAction() && ( m_xData->maUndoArray.nCurUndoAction == 1 ) )
{ // this can happen if we are performing a very large writer edit (e.g. removing a very large table)
SAL_WARN("svl", "SfxUndoManager::RemoveOldestUndoActions: cannot remove a not-yet-closed list action!"); returnfalse;
}
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 und die Messung sind noch experimentell.