summaryrefslogtreecommitdiff
path: root/sw/source/core/undo/docundo.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/undo/docundo.cxx')
-rw-r--r--sw/source/core/undo/docundo.cxx1133
1 files changed, 343 insertions, 790 deletions
diff --git a/sw/source/core/undo/docundo.cxx b/sw/source/core/undo/docundo.cxx
index 85adbd1c7138..3dce09e66fa9 100644
--- a/sw/source/core/undo/docundo.cxx
+++ b/sw/source/core/undo/docundo.cxx
@@ -28,1000 +28,553 @@
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"
-#include <svx/svdmodel.hxx>
+#include <UndoManager.hxx>
+
+#include <unotools/undoopt.hxx>
#include <vcl/wrkwin.hxx>
+
+#include <svx/svdmodel.hxx>
+
+#include <swmodule.hxx>
#include <doc.hxx>
+#include <ndarr.hxx>
#include <pam.hxx>
#include <ndtxt.hxx>
-#include <swundo.hxx> // fuer die UndoIds
-#include <undobj.hxx>
+#include <swundo.hxx>
+#include <UndoCore.hxx>
#include <rolbck.hxx>
-#include <docary.hxx>
-#ifndef _UNDO_HRC
#include <undo.hrc>
-#endif
+#include <editsh.hxx>
+#include <unobaseclass.hxx>
using namespace ::com::sun::star;
-USHORT SwDoc::nUndoActions = UNDO_ACTION_COUNT; // anzahl von Undo-Action
-
// the undo array should never grow beyond this limit:
#define UNDO_ACTION_LIMIT (USHRT_MAX - 1000)
-SV_IMPL_PTRARR( SwUndoIds, SwUndoIdAndNamePtr )
-
-//#define _SHOW_UNDORANGE
-#ifdef _SHOW_UNDORANGE
+// UndoManager ///////////////////////////////////////////////////////////
+namespace sw {
-class UndoArrStatus : public WorkWindow
+UndoManager::UndoManager(::std::auto_ptr<SwNodes> pUndoNodes,
+ IDocumentDrawModelAccess & rDrawModelAccess,
+ IDocumentRedlineAccess & rRedlineAccess,
+ IDocumentState & rState)
+ : m_rDrawModelAccess(rDrawModelAccess)
+ , m_rRedlineAccess(rRedlineAccess)
+ , m_rState(rState)
+ , m_pUndoNodes(pUndoNodes)
+ , m_bGroupUndo(true)
+ , m_bDrawUndo(true)
+ , m_bLockUndoNoModifiedPosition(false)
+ , m_UndoSaveMark(MARK_INVALID)
{
- USHORT nUndo, nUndoNds;
- virtual void Paint( const Rectangle& );
-public:
- UndoArrStatus();
- void Set( USHORT, USHORT );
-};
-static UndoArrStatus* pUndoMsgWin = 0;
-
-
-UndoArrStatus::UndoArrStatus()
- : WorkWindow( APP_GETAPPWINDOW() ), nUndo(0), nUndoNds(0)
-{
- SetSizePixel( Size( 200, 100 ));
- SetFont( Font( "Courier", Size( 0, 10 )) );
- Show();
+ OSL_ASSERT(m_pUndoNodes.get());
+ // writer expects it to be disabled initially
+ // Undo is enabled by SwEditShell constructor
+ SfxUndoManager::EnableUndo(false);
}
-
-void UndoArrStatus::Set( USHORT n1, USHORT n2 )
+SwNodes const& UndoManager::GetUndoNodes() const
{
- nUndo = n1; nUndoNds = n2;
- Invalidate();
+ return *m_pUndoNodes;
}
-
-void UndoArrStatus::Paint( const Rectangle& )
+SwNodes & UndoManager::GetUndoNodes()
{
- String s;
- DrawRect( Rectangle( Point(0,0), GetOutputSize() ));
- ( s = "Undos: " ) += nUndo;
- DrawText( Point( 0, 0 ), s );
- ( s = "UndoNodes: " ) += nUndoNds;
- DrawText( Point( 0, 15 ), s );
+ return *m_pUndoNodes;
}
-#endif
-
-void SwDoc::SetUndoNoResetModified()
+bool UndoManager::IsUndoNodes(SwNodes const& rNodes) const
{
- nUndoSavePos = USHRT_MAX;
+ return & rNodes == m_pUndoNodes.get();
}
-bool SwDoc::IsUndoNoResetModified() const
+void UndoManager::DoUndo(bool const bDoUndo)
{
- return USHRT_MAX == nUndoSavePos;
-}
+ EnableUndo(bDoUndo);
-void SwDoc::DoUndo(bool bUn)
-{
- mbUndo = bUn;
-
- SdrModel* pSdrModel = GetDrawModel();
+ SdrModel *const pSdrModel = m_rDrawModelAccess.GetDrawModel();
if( pSdrModel )
- pSdrModel->EnableUndo(bUn);
+ {
+ pSdrModel->EnableUndo(bDoUndo);
+ }
}
-bool SwDoc::DoesUndo() const
+bool UndoManager::DoesUndo() const
{
- return mbUndo;
+ return IsUndoEnabled();
}
-void SwDoc::DoGroupUndo(bool bUn)
+void UndoManager::DoGroupUndo(bool const bDoUndo)
{
- mbGroupUndo = bUn;
+ m_bGroupUndo = bDoUndo;
}
-bool SwDoc::DoesGroupUndo() const
+bool UndoManager::DoesGroupUndo() const
{
- return mbGroupUndo;
+ return m_bGroupUndo;
}
-sal_uInt16 SwDoc::GetUndoActionCount()
+void UndoManager::DoDrawUndo(bool const bDoUndo)
{
- return nUndoActions;
+ m_bDrawUndo = bDoUndo;
}
-void SwDoc::SetUndoActionCount( sal_uInt16 nNew )
+bool UndoManager::DoesDrawUndo() const
{
- nUndoActions = nNew;
+ return m_bDrawUndo;
}
-const SwNodes* SwDoc::GetUndoNds() const
-{
- return &aUndoNodes;
-}
-void SwDoc::AppendUndo( SwUndo* pUndo )
+bool UndoManager::IsUndoNoResetModified() const
{
- if( nsRedlineMode_t::REDLINE_NONE == pUndo->GetRedlineMode() )
- pUndo->SetRedlineMode( GetRedlineMode() );
-
- // Unfortunately, the silly SvPtrArr can only store a little less than
- // USHRT_MAX elements. Of course it doesn't see any necessity for asserting
- // or even doing error handling. pUndos should definitely be replaced by an
- // STL container that doesn't have this problem. cf #95884#
- DBG_ASSERT( pUndos->Count() < USHRT_MAX - 16,
- "Writer will crash soon. I apologize for the inconvenience." );
-
- pUndos->Insert( pUndo, nUndoPos );
- ++nUndoPos;
- switch( pUndo->GetId() )
- {
- case UNDO_START: ++nUndoSttEnd;
- break;
-
- case UNDO_END: ASSERT( nUndoSttEnd, "Undo-Ende ohne Start" );
- --nUndoSttEnd;
- // kein break !!!
- default:
- if( pUndos->Count() != nUndoPos && UNDO_END != pUndo->GetId() )
- ClearRedo();
- else {
- ASSERT( pUndos->Count() == nUndoPos || UNDO_END == pUndo->GetId(),
- "Redo history not deleted!" );
- }
- if( !nUndoSttEnd )
- ++nUndoCnt;
- break;
- }
-
-#ifdef _SHOW_UNDORANGE
- // zur Anzeige der aktuellen Undo-Groessen
- if( !pUndoMsgWin )
- pUndoMsgWin = new UndoArrStatus;
- pUndoMsgWin->Set( pUndos->Count(), aUndoNodes.Count() );
-#endif
-
- // noch eine offene Klammerung, kann man sich den Rest schenken
- if( nUndoSttEnd )
- return;
-
- // folgende Array-Grenzen muessen ueberwacht werden:
- // - Undo, Grenze: fester Wert oder USHRT_MAX - 1000
- // - UndoNodes, Grenze: USHRT_MAX - 1000
- // - AttrHistory Grenze: USHRT_MAX - 1000
- // (defined in UNDO_ACTION_LIMIT at the top of this file)
-
- USHORT nEnde = UNDO_ACTION_LIMIT;
+ return MARK_INVALID == m_UndoSaveMark;
+}
-// nur zum Testen der neuen DOC-Member
-#ifdef DBG_UTIL
+void UndoManager::SetUndoNoResetModified()
{
- SwUndoId nId = UNDO_EMPTY;
- USHORT nUndosCnt = 0, nSttEndCnt = 0;
- for( USHORT nCnt = 0; nCnt < nUndoPos; ++nCnt )
+ if (MARK_INVALID != m_UndoSaveMark)
{
- if( UNDO_START == ( nId = (*pUndos)[ nCnt ]->GetId()) )
- ++nSttEndCnt;
- else if( UNDO_END == nId )
- --nSttEndCnt;
- if( !nSttEndCnt )
- ++nUndosCnt;
+ RemoveMark(m_UndoSaveMark);
+ m_UndoSaveMark = MARK_INVALID;
}
- ASSERT( nSttEndCnt == nUndoSttEnd, "Start-Ende Count ungleich" );
- ASSERT( nUndosCnt == nUndoCnt, "Undo Count ungleich" );
}
-#endif
- if( SwDoc::nUndoActions < nUndoCnt )
- // immer 1/10 loeschen
- //JP 23.09.95: oder wenn neu eingestellt wurde um die Differenz
- //JP 29.5.2001: Task #83891#: remove only the overlapping actions
- DelUndoObj( nUndoCnt - SwDoc::nUndoActions );
- else
+void UndoManager::SetUndoNoModifiedPosition()
+{
+ if (!m_bLockUndoNoModifiedPosition)
{
- USHORT nUndosCnt = nUndoCnt;
- // immer 1/10 loeschen bis der "Ausloeser" behoben ist
- while( aUndoNodes.Count() && nEnde < aUndoNodes.Count() )
- DelUndoObj( nUndosCnt / 10 );
+ m_UndoSaveMark = MarkTopUndoAction();
}
}
-
-
-void SwDoc::ClearRedo()
+void UndoManager::LockUndoNoModifiedPosition()
{
- if( DoesUndo() && nUndoPos != pUndos->Count() )
- {
-//?? why ?? if( !nUndoSttEnd )
- {
- // setze UndoCnt auf den neuen Wert
- SwUndo* pUndo;
- for( USHORT nCnt = pUndos->Count(); nUndoPos < nCnt; --nUndoCnt )
- // Klammerung ueberspringen
- if( UNDO_END == (pUndo = (*pUndos)[ --nCnt ])->GetId() )
- nCnt = nCnt - ((SwUndoEnd*)pUndo)->GetSttOffset();
- }
-
- // loesche die Undo-Aktionen (immer von hinten !)
- pUndos->DeleteAndDestroy( nUndoPos, pUndos->Count() - nUndoPos );
- }
+ m_bLockUndoNoModifiedPosition = true;
}
-
- // loescht die gesamten UndoObjecte
-void SwDoc::DelAllUndoObj()
+void UndoManager::UnLockUndoNoModifiedPosition()
{
- ClearRedo();
-
- DoUndo( FALSE );
-
- // Offene Undo-Klammerungen erhalten !!
- SwUndo* pUndo;
- USHORT nSize = pUndos->Count();
- while( nSize )
- if( UNDO_START != ( pUndo = (*pUndos)[ --nSize ] )->GetId() ||
- ((SwUndoStart*)pUndo)->GetEndOffset() )
- // keine offenen Gruppierung ?
- pUndos->DeleteAndDestroy( nSize, 1 );
-
- nUndoCnt = 0;
- nUndoPos = pUndos->Count();
-
-/*
- while( nUndoPos )
- aUndos.DelDtor( --nUndoPos, 1 );
- nUndoCnt = nUndoSttEnd = nUndoPos = 0;
-*/
- nUndoSavePos = USHRT_MAX;
- DoUndo( TRUE );
+ m_bLockUndoNoModifiedPosition = false;
}
- // loescht alle UndoObjecte vom Anfang bis zum angegebenen Ende
-BOOL SwDoc::DelUndoObj( USHORT nEnde )
+SwUndo* UndoManager::GetLastUndo()
{
- if( !nEnde ) // sollte mal 0 uebergeben werden,
+ if (!SfxUndoManager::GetUndoActionCount(CurrentLevel))
{
- if( !pUndos->Count() )
- return FALSE;
- ++nEnde; // dann korrigiere es auf 1
+ return 0;
}
-
- DoUndo( FALSE );
-
- // pruefe erstmal, wo das Ende steht
- SwUndoId nId = UNDO_EMPTY;
- USHORT nSttEndCnt = 0;
- USHORT nCnt;
-
- for( nCnt = 0; nEnde && nCnt < nUndoPos; ++nCnt )
- {
- if( UNDO_START == ( nId = (*pUndos)[ nCnt ]->GetId() ))
- ++nSttEndCnt;
- else if( UNDO_END == nId )
- --nSttEndCnt;
- if( !nSttEndCnt )
- --nEnde, --nUndoCnt;
- }
-
- ASSERT( nCnt < nUndoPos || nUndoPos == pUndos->Count(),
- "Undo-Del-Ende liegt in einer Redo-Aktion" );
-
- // dann setze ab Ende bis Undo-Ende bei allen Undo-Objecte die Werte um
- nSttEndCnt = nCnt; // Position merken
- if( nUndoSavePos < nSttEndCnt ) // SavePos wird aufgegeben
- nUndoSavePos = USHRT_MAX;
- else if( nUndoSavePos != USHRT_MAX )
- nUndoSavePos = nUndoSavePos - nSttEndCnt;
-
- while( nSttEndCnt )
- pUndos->DeleteAndDestroy( --nSttEndCnt, 1 );
- nUndoPos = pUndos->Count();
-
- DoUndo( TRUE );
- return TRUE;
+ SfxUndoAction *const pAction( SfxUndoManager::GetUndoAction(0) );
+ return dynamic_cast<SwUndo*>(pAction);
}
-/**************** UNDO ******************/
-
-void SwDoc::setUndoNoModifiedPosition( SwUndoNoModifiedPosition nNew )
-{
- nUndoSavePos = nNew;
- if( !pUndos->Count() || nUndoSavePos > pUndos->Count() - 1 )
- nUndoSavePos = USHRT_MAX;
-}
-
-SwUndoNoModifiedPosition SwDoc::getUndoNoModifiedPosition() const
+void UndoManager::AppendUndo(SwUndo *const pUndo)
{
- return nUndoSavePos;
+ AddUndoAction(pUndo);
}
-
-bool SwDoc::HasUndoId(SwUndoId eId) const
+void UndoManager::ClearRedo()
{
- USHORT nSize = nUndoPos;
- SwUndo * pUndo;
- while( nSize-- )
- if( ( pUndo = (*pUndos)[nSize])->GetId() == eId ||
- ( UNDO_START == pUndo->GetId() &&
- ((SwUndoStart*)pUndo)->GetUserId() == eId )
- || ( UNDO_END == pUndo->GetId() &&
- ((SwUndoEnd*)pUndo)->GetUserId() == eId ) )
- {
- return TRUE;
- }
-
- return FALSE;
+ return SfxUndoManager::ImplClearRedo_NoLock(TopLevel);
}
-
-bool SwDoc::Undo( SwUndoIter& rUndoIter )
+void UndoManager::DelAllUndoObj()
{
- if ( (rUndoIter.GetId()!=0) && (!HasUndoId(rUndoIter.GetId())) )
- {
- rUndoIter.bWeiter = FALSE;
- return FALSE;
- }
- if( !nUndoPos )
- {
- rUndoIter.bWeiter = FALSE;
- return FALSE;
- }
-
- SwUndo *pUndo = (*pUndos)[ --nUndoPos ];
+ ::sw::UndoGuard const undoGuard(*this);
- RedlineMode_t eOld = GetRedlineMode();
- RedlineMode_t eTmpMode = (RedlineMode_t)pUndo->GetRedlineMode();
- if( (nsRedlineMode_t::REDLINE_SHOW_MASK & eTmpMode) != (nsRedlineMode_t::REDLINE_SHOW_MASK & eOld) &&
- UNDO_START != pUndo->GetId() && UNDO_END != pUndo->GetId() )
- SetRedlineMode( eTmpMode );
+ SfxUndoManager::ClearAllLevels();
- SetRedlineMode_intern((RedlineMode_t)(eTmpMode | nsRedlineMode_t::REDLINE_IGNORE));
- // Undo ausfuehren
-
- // zum spaeteren ueberpruefen
- SwUndoId nAktId = pUndo->GetId();
- //JP 11.05.98: FlyFormate ueber die EditShell selektieren, nicht aus dem
- // Undo heraus
- switch( nAktId )
- {
- case UNDO_START:
- case UNDO_END:
- case UNDO_INSDRAWFMT:
- break;
-
- default:
- rUndoIter.ClearSelections();
- }
-
- pUndo->Undo( rUndoIter );
-
- SetRedlineMode( eOld );
-
- // Besonderheit von Undo-Replace (interne History)
- if( UNDO_REPLACE == nAktId && ((SwUndoReplace*)pUndo)->nAktPos )
- {
- ++nUndoPos;
- return TRUE;
- }
-
- // Objekt aus History entfernen und zerstoeren
- if( nUndoPos && !rUndoIter.bWeiter &&
- UNDO_START == ( pUndo = (*pUndos)[ nUndoPos-1 ] )->GetId() )
- --nUndoPos;
-
- // JP 29.10.96: Start und End setzen kein Modify-Flag.
- // Sonst gibt es Probleme mit der autom. Aufnahme von Ausnahmen
- // bei der Autokorrektur
- if( UNDO_START != nAktId && UNDO_END != nAktId )
- SetModified(); // default: immer setzen, kann zurueck gesetzt werden
-
- // ist die History leer und wurde nicht wegen Speichermangel
- // verworfen, so kann das Dokument als unveraendert gelten
- if( nUndoSavePos == nUndoPos )
- ResetModified();
-
- return TRUE;
+ m_UndoSaveMark = MARK_INVALID;
}
-// setzt Undoklammerung auf, liefert nUndoId der Klammerung
-
+/**************** UNDO ******************/
-SwUndoId SwDoc::StartUndo( SwUndoId eUndoId, const SwRewriter * pRewriter )
+SwUndoId
+UndoManager::StartUndo(SwUndoId const i_eUndoId,
+ SwRewriter const*const pRewriter)
{
- if( !mbUndo )
+ if (!IsUndoEnabled())
+ {
return UNDO_EMPTY;
+ }
- if( !eUndoId )
- eUndoId = UNDO_START;
-
- SwUndoStart * pUndo = new SwUndoStart( eUndoId );
+ SwUndoId const eUndoId( (0 == i_eUndoId) ? UNDO_START : i_eUndoId );
+ OSL_ASSERT(UNDO_END != eUndoId);
+ String comment( (UNDO_START == eUndoId)
+ ? String("??", RTL_TEXTENCODING_ASCII_US)
+ : String(SW_RES(UNDO_BASE + eUndoId)) );
if (pRewriter)
- pUndo->SetRewriter(*pRewriter);
+ {
+ OSL_ASSERT(UNDO_START != eUndoId);
+ comment = pRewriter->Apply(comment);
+ }
- AppendUndo(pUndo);
+ SfxUndoManager::EnterListAction(comment, comment, eUndoId);
return eUndoId;
}
-// schliesst Klammerung der nUndoId, nicht vom UI benutzt
-SwUndoId SwDoc::EndUndo(SwUndoId eUndoId, const SwRewriter * pRewriter)
+SwUndoId
+UndoManager::EndUndo(SwUndoId const i_eUndoId, SwRewriter const*const pRewriter)
{
- USHORT nSize = nUndoPos;
- if( !mbUndo || !nSize-- )
- return UNDO_EMPTY;
-
- if( UNDO_START == eUndoId || !eUndoId )
- eUndoId = UNDO_END;
-
- SwUndo* pUndo = (*pUndos)[ nSize ];
- if( UNDO_START == pUndo->GetId() )
+ if (!IsUndoEnabled())
{
- // leere Start/End-Klammerung ??
- pUndos->DeleteAndDestroy( nSize );
- --nUndoPos;
- --nUndoSttEnd;
return UNDO_EMPTY;
}
- // exist above any redo objects? If yes, delete them
- if( nUndoPos != pUndos->Count() )
- {
- // setze UndoCnt auf den neuen Wert
- for( USHORT nCnt = pUndos->Count(); nUndoPos < nCnt; --nUndoCnt )
- // Klammerung ueberspringen
- if( UNDO_END == (pUndo = (*pUndos)[ --nCnt ])->GetId() )
- nCnt = nCnt - ((SwUndoEnd*)pUndo)->GetSttOffset();
-
- pUndos->DeleteAndDestroy( nUndoPos, pUndos->Count() - nUndoPos );
- }
-
- // suche den Anfang dieser Klammerung
- SwUndoId nId = UNDO_EMPTY;
- while( nSize )
- if( UNDO_START == ( nId = (pUndo = (*pUndos)[ --nSize ] )->GetId()) &&
- !((SwUndoStart*)pUndo)->GetEndOffset() )
- break; // Start gefunden
+ SwUndoId const eUndoId( ((0 == i_eUndoId) || (UNDO_START == i_eUndoId))
+ ? UNDO_END : i_eUndoId );
+ OSL_ENSURE(!((UNDO_END == eUndoId) && pRewriter),
+ "EndUndo(): no Undo ID, but rewriter given?");
- if( nId != UNDO_START )
- {
- // kann eigentlich nur beim Abspielen von Macros passieren, die
- // Undo/Redo/Repeat benutzen und die eine exitierende Selection
- // durch Einfuegen loeschen
- ASSERT( !this, "kein entsprechendes Ende gefunden" );
- // kein entsprechenden Start gefunden -> Ende nicht einfuegen
- // und die Member am Doc updaten
-
- nUndoSttEnd = 0;
- nUndoCnt = 0;
- // setze UndoCnt auf den neuen Wert
- SwUndo* pTmpUndo;
- for( USHORT nCnt = 0; nCnt < pUndos->Count(); ++nCnt, ++nUndoCnt )
- // Klammerung ueberspringen
- if( UNDO_START == (pTmpUndo = (*pUndos)[ nCnt ])->GetId() )
- nCnt = nCnt + ((SwUndoStart*)pTmpUndo)->GetEndOffset();
- return UNDO_EMPTY;
+ SfxUndoAction *const pLastUndo(
+ (0 == SfxUndoManager::GetUndoActionCount(CurrentLevel))
+ ? 0 : SfxUndoManager::GetUndoAction(0) );
- }
+ int const nCount = LeaveListAction();
- // Klammerung um eine einzelne Action muss nicht sein!
- // Aussnahme: es ist eine eigene ID definiert
- if( 2 == pUndos->Count() - nSize &&
- (UNDO_END == eUndoId || eUndoId == (*pUndos)[ nSize+1 ]->GetId() ))
+ if (nCount) // otherwise: empty list action not inserted!
{
- pUndos->DeleteAndDestroy( nSize );
- nUndoPos = pUndos->Count();
- if( !--nUndoSttEnd )
+ OSL_ASSERT(pLastUndo);
+ OSL_ASSERT(UNDO_START != eUndoId);
+ SfxUndoAction *const pUndoAction(SfxUndoManager::GetUndoAction(0));
+ SfxListUndoAction *const pListAction(
+ dynamic_cast<SfxListUndoAction*>(pUndoAction));
+ OSL_ASSERT(pListAction);
+ if (pListAction)
{
- ++nUndoCnt;
- if( SwDoc::nUndoActions < nUndoCnt )
- // immer 1/10 loeschen
- //JP 23.09.95: oder wenn neu eingestellt wurde um die Differenz
- //JP 29.5.2001: Task #83891#: remove only the overlapping actions
- DelUndoObj( nUndoCnt - SwDoc::nUndoActions );
+ if (UNDO_END != eUndoId)
+ {
+ OSL_ENSURE(pListAction->GetId() == eUndoId,
+ "EndUndo(): given ID different from StartUndo()");
+ // comment set by caller of EndUndo
+ String comment = String(SW_RES(UNDO_BASE + eUndoId));
+ if (pRewriter)
+ {
+ comment = pRewriter->Apply(comment);
+ }
+ pListAction->SetComment(comment);
+ }
+ else if ((UNDO_START != pListAction->GetId()))
+ {
+ // comment set by caller of StartUndo: nothing to do here
+ }
+ else if (pLastUndo)
+ {
+ // comment was not set at StartUndo or EndUndo:
+ // take comment of last contained action
+ // (note that this works recursively, i.e. the last contained
+ // action may be a list action created by StartUndo/EndUndo)
+ String const comment(pLastUndo->GetComment());
+ pListAction->SetComment(comment);
+ }
else
{
- USHORT nEnde = USHRT_MAX - 1000;
- USHORT nUndosCnt = nUndoCnt;
- // immer 1/10 loeschen bis der "Ausloeser" behoben ist
- while( aUndoNodes.Count() && nEnde < aUndoNodes.Count() )
- DelUndoObj( nUndosCnt / 10 );
+ OSL_ENSURE(false, "EndUndo(): no comment?");
}
}
- return eUndoId;
}
- // setze die Klammerung am Start/End-Undo
- nSize = pUndos->Count() - nSize;
- ((SwUndoStart*)pUndo)->SetEndOffset( nSize );
-
- SwUndoEnd* pUndoEnd = new SwUndoEnd( eUndoId );
- pUndoEnd->SetSttOffset( nSize );
+ return eUndoId;
+}
-// nur zum Testen der Start/End-Verpointerung vom Start/End Undo
-#ifdef DBG_UTIL
+bool
+UndoManager::GetLastUndoInfo(
+ ::rtl::OUString *const o_pStr, SwUndoId *const o_pId) const
+{
+ // this is actually expected to work on the current level,
+ // but that was really not obvious from the previous implementation...
+ if (!SfxUndoManager::GetUndoActionCount(CurrentLevel))
{
- USHORT nEndCnt = 1, nCnt = pUndos->Count();
- SwUndoId nTmpId = UNDO_EMPTY;
- while( nCnt )
- {
- if( UNDO_START == ( nTmpId = (*pUndos)[ --nCnt ]->GetId()) )
- {
- if( !nEndCnt ) // falls mal ein Start ohne Ende vorhanden ist
- continue;
- --nEndCnt;
- if( !nEndCnt ) // hier ist der Anfang
- break;
- }
- else if( UNDO_END == nTmpId )
- ++nEndCnt;
- else if( !nEndCnt )
- break;
- }
- ASSERT( nCnt == pUndos->Count() - nSize,
- "Start-Ende falsch geklammert" );
+ return false;
}
-#endif
- if (pRewriter)
+ SfxUndoAction *const pAction( SfxUndoManager::GetUndoAction(0) );
+
+ if (o_pStr)
{
- ((SwUndoStart *) pUndo)->SetRewriter(*pRewriter);
- pUndoEnd->SetRewriter(*pRewriter);
+ *o_pStr = pAction->GetComment();
+ }
+ if (o_pId)
+ {
+ USHORT const nId(pAction->GetId());
+ *o_pId = static_cast<SwUndoId>(nId);
}
- else
- pUndoEnd->SetRewriter(((SwUndoStart *) pUndo)->GetRewriter());
- AppendUndo( pUndoEnd );
- return eUndoId;
+ return true;
}
-// liefert die Id der letzten Undofaehigen Aktion zurueck oder 0
-// fuellt ggf. VARARR mit User-UndoIds
-
-String SwDoc::GetUndoIdsStr( String* pStr, SwUndoIds *pUndoIds) const
+SwUndoComments_t UndoManager::GetUndoComments() const
{
- String aTmpStr;
+ OSL_ENSURE(!SfxUndoManager::IsInListAction(),
+ "GetUndoComments() called while in list action?");
- if (pStr != NULL)
+ SwUndoComments_t ret;
+ USHORT const nUndoCount(SfxUndoManager::GetUndoActionCount(TopLevel));
+ for (USHORT n = 0; n < nUndoCount; ++n)
{
- GetUndoIds( pStr, pUndoIds);
- aTmpStr = *pStr;
+ ::rtl::OUString const comment(
+ SfxUndoManager::GetUndoActionComment(n, TopLevel));
+ ret.push_back(comment);
}
- else
- GetUndoIds( &aTmpStr, pUndoIds);
- return aTmpStr;
+ return ret;
}
-/*-- 24.11.2004 16:11:21---------------------------------------------------
- -----------------------------------------------------------------------*/
-sal_Bool SwDoc::RestoreInvisibleContent()
+/**************** REDO ******************/
+
+bool UndoManager::GetFirstRedoInfo(::rtl::OUString *const o_pStr) const
{
- sal_Bool bRet = sal_False;
- if(nUndoPos > 0 )
+ if (!SfxUndoManager::GetRedoActionCount(CurrentLevel))
{
- SwUndo * pUndo = (*pUndos)[ nUndoPos - 1 ];
- if( ( pUndo->GetId() == UNDO_END &&
- static_cast<SwUndoEnd *>(pUndo)->GetUserId() == UNDO_UI_DELETE_INVISIBLECNTNT) )
- {
- SwPaM aPam( GetNodes().GetEndOfPostIts() );
- SwUndoIter aUndoIter( &aPam );
- do
- {
- Undo( aUndoIter );
- }
- while ( aUndoIter.IsNextUndo() );
- ClearRedo();
- bRet = sal_True;
- }
+ return false;
}
- return bRet;
-}
-
-
-/**
- Returns id and comment for a certain undo object in an undo stack.
-
- Remark: In the following the object type referred to is always the
- effective object type. If an UNDO_START or UNDO_END has a user type
- it is referred to as this type.
-
- If the queried object is an UNDO_END and has no user id the result
- is taken from the first object that is not an UNDO_END nor an
- UNDO_START preceeding the queried object.
-
- If the queried object is an UNDO_START and has no user id the
- result is taken from the first object that is not an UNDO_END nor
- an UNDO_START preceeding the UNDO_END object belonging to the
- queried object.
- In all other cases the result is taken from the queried object.
-
- @param rUndos the undo stack
- @param nPos position of the undo object to query
-
- @return SwUndoIdAndName object containing the query result
- */
-SwUndoIdAndName * lcl_GetUndoIdAndName(const SwUndos & rUndos, sal_uInt16 nPos )
-{
- SwUndo * pUndo = rUndos[ nPos ];
- SwUndoId nId = UNDO_EMPTY;
- String sStr("??", RTL_TEXTENCODING_ASCII_US);
-
- ASSERT( nPos < rUndos.Count(), "nPos out of range");
-
- switch (pUndo->GetId())
+ if (o_pStr)
{
- case UNDO_START:
- {
- SwUndoStart * pUndoStart = (SwUndoStart *) pUndo;
- nId = pUndoStart->GetUserId();
-
- if (nId <= UNDO_END)
- {
- /**
- Start at the according UNDO_END. Search backwards
- for first objects that is not a UNDO_END.
- */
- int nTmpPos = nPos + pUndoStart->GetEndOffset();
- int nSubstitute = -1;
-
- // --> OD 2009-09-30 #i105457#
- if ( nTmpPos > 0 )
- // <--
- {
- SwUndo * pTmpUndo;
- do
- {
- nTmpPos--;
- pTmpUndo = rUndos[ static_cast<USHORT>(nTmpPos) ];
-
- if (pTmpUndo->GetEffectiveId() > UNDO_END)
- nSubstitute = nTmpPos;
- }
- while (nSubstitute < 0 && nTmpPos > nPos);
-
- if (nSubstitute >= 0)
- {
- SwUndo * pSubUndo = rUndos[ static_cast<USHORT>(nSubstitute) ];
- nId = pSubUndo->GetEffectiveId();
- sStr = pSubUndo->GetComment();
- }
- }
- }
- else
- sStr = pUndo->GetComment();
- }
-
- break;
-
- case UNDO_END:
- {
- SwUndoEnd * pUndoEnd = (SwUndoEnd *) pUndo;
- nId = pUndoEnd->GetUserId();
-
- if (nId <= UNDO_END)
- {
- /**
- Start at this UNDO_END. Search backwards
- for first objects that is not a UNDO_END.
- */
-
- int nTmpPos = nPos;
- int nUndoStart = nTmpPos - pUndoEnd->GetSttOffset();
- int nSubstitute = -1;
-
- if (nTmpPos > 0)
- {
- SwUndo * pTmpUndo;
-
- do
- {
- nTmpPos--;
- pTmpUndo = rUndos[ static_cast<USHORT>(nTmpPos) ];
-
- if (pTmpUndo->GetEffectiveId() > UNDO_END)
- nSubstitute = nTmpPos;
- }
- while (nSubstitute < 0 && nTmpPos > nUndoStart);
-
- if (nSubstitute >= 0)
- {
- SwUndo * pSubUndo = rUndos[ static_cast<USHORT>(nSubstitute) ];
- nId = pSubUndo->GetEffectiveId();
- sStr = pSubUndo->GetComment();
- }
- }
- }
- else
- sStr = pUndo->GetComment();
- }
-
- break;
-
- default:
- nId = pUndo->GetId();
- sStr = pUndo->GetComment();
+ *o_pStr = SfxUndoManager::GetRedoActionComment(0, CurrentLevel);
}
- return new SwUndoIdAndName(nId, &sStr);
+ return true;
}
-SwUndoId SwDoc::GetUndoIds( String* pStr, SwUndoIds *pUndoIds) const
+
+SwUndoComments_t UndoManager::GetRedoComments() const
{
- int nTmpPos = nUndoPos - 1;
- SwUndoId nId = UNDO_EMPTY;
+ OSL_ENSURE(!SfxUndoManager::IsInListAction(),
+ "GetRedoComments() called while in list action?");
- while (nTmpPos >= 0)
+ SwUndoComments_t ret;
+ USHORT const nRedoCount(SfxUndoManager::GetRedoActionCount(TopLevel));
+ for (USHORT n = 0; n < nRedoCount; ++n)
{
- SwUndo * pUndo = (*pUndos)[ static_cast<USHORT>(nTmpPos) ];
-
- SwUndoIdAndName * pIdAndName = lcl_GetUndoIdAndName( *pUndos, static_cast<sal_uInt16>(nTmpPos) );
-
- if (nTmpPos == nUndoPos - 1)
- {
- nId = pIdAndName->GetUndoId();
-
- if (pStr)
- *pStr = *pIdAndName->GetUndoStr();
- }
-
- if (pUndoIds)
- pUndoIds->Insert(pIdAndName, pUndoIds->Count());
- else
- break;
-
- if (pUndo->GetId() == UNDO_END)
- nTmpPos -= ((SwUndoEnd *) pUndo)->GetSttOffset();
-
- nTmpPos--;
+ ::rtl::OUString const comment(
+ SfxUndoManager::GetRedoActionComment(n, TopLevel));
+ ret.push_back(comment);
}
- return nId;
+ return ret;
}
-bool SwDoc::HasTooManyUndos() const
-{
- // AppendUndo checks the UNDO_ACTION_LIMIT, unless there's a nested undo.
- // So HasTooManyUndos() may only occur when undos are nested; else
- // AppendUndo has some sort of bug.
- DBG_ASSERT( (nUndoSttEnd != 0) || (pUndos->Count() < UNDO_ACTION_LIMIT),
- "non-nested undos should have been handled in AppendUndo" );
- return (pUndos->Count() >= UNDO_ACTION_LIMIT);
-}
-
-
-/**************** REDO ******************/
-
+/**************** REPEAT ******************/
-bool SwDoc::Redo( SwUndoIter& rUndoIter )
+SwUndoId UndoManager::GetRepeatInfo(::rtl::OUString *const o_pStr) const
{
- if( rUndoIter.GetId() && !HasUndoId( rUndoIter.GetId() ) )
+ SwUndoId nRepeatId(UNDO_EMPTY);
+ GetLastUndoInfo(o_pStr, & nRepeatId);
+ if( REPEAT_START <= nRepeatId && REPEAT_END > nRepeatId )
{
- rUndoIter.bWeiter = FALSE;
- return FALSE;
+ return nRepeatId;
}
- if( nUndoPos == pUndos->Count() )
+ if (o_pStr) // not repeatable -> clear comment
{
- rUndoIter.bWeiter = FALSE;
- return FALSE;
+ *o_pStr = String();
}
+ return UNDO_EMPTY;
+}
- SwUndo *pUndo = (*pUndos)[ nUndoPos++ ];
-
- RedlineMode_t eOld = GetRedlineMode();
- RedlineMode_t eTmpMode = (RedlineMode_t)pUndo->GetRedlineMode();
- if( (nsRedlineMode_t::REDLINE_SHOW_MASK & eTmpMode) != (nsRedlineMode_t::REDLINE_SHOW_MASK & eOld) &&
- UNDO_START != pUndo->GetId() && UNDO_END != pUndo->GetId() )
- SetRedlineMode( eTmpMode );
- SetRedlineMode_intern( (RedlineMode_t)(eTmpMode | nsRedlineMode_t::REDLINE_IGNORE));
-
- //JP 11.05.98: FlyFormate ueber die EditShell selektieren, nicht aus dem
- // Undo heraus
- if( UNDO_START != pUndo->GetId() && UNDO_END != pUndo->GetId() )
- rUndoIter.ClearSelections();
-
- pUndo->Redo( rUndoIter );
-
- SetRedlineMode( eOld );
-
- // Besonderheit von Undo-Replace (interne History)
- if( UNDO_REPLACE == pUndo->GetId() &&
- USHRT_MAX != ((SwUndoReplace*)pUndo)->nAktPos )
+SwUndo * UndoManager::RemoveLastUndo()
+{
+ if (SfxUndoManager::GetRedoActionCount(CurrentLevel) ||
+ SfxUndoManager::GetRedoActionCount(TopLevel))
{
- --nUndoPos;
- return TRUE;
+ OSL_ENSURE(false, "RemoveLastUndoAction(): there are Redo actions?");
+ return 0;
}
-
- if( rUndoIter.bWeiter && nUndoPos >= pUndos->Count() )
- rUndoIter.bWeiter = FALSE;
-
- // ist die History leer und wurde nicht wegen Speichermangel
- // verworfen, so kann das Dokument als unveraendert gelten
- if( nUndoSavePos == nUndoPos )
- ResetModified();
- else
- SetModified();
- return TRUE;
+ if (!SfxUndoManager::GetUndoActionCount(CurrentLevel))
+ {
+ OSL_ENSURE(false, "RemoveLastUndoAction(): no Undo actions");
+ return 0;
+ }
+ SfxUndoAction *const pLastUndo(GetUndoAction(0));
+ SfxUndoManager::RemoveLastUndoAction();
+ return dynamic_cast<SwUndo *>(pLastUndo);
}
+// svl::IUndoManager /////////////////////////////////////////////////////
-// liefert die Id der letzten Redofaehigen Aktion zurueck oder 0
-// fuellt ggf. VARARR mit User-RedoIds
-
-String SwDoc::GetRedoIdsStr( String* pStr, SwUndoIds *pRedoIds ) const
+void UndoManager::EnableUndo(bool bEnable)
{
- String aTmpStr;
-
- if (pStr != NULL)
+ // UGLY: SfxUndoManager has a counter to match enable/disable calls
+ // but the writer code expects that a single call switches
+ while (IsUndoEnabled() != bEnable)
{
- GetRedoIds( pStr, pRedoIds );
- aTmpStr = *pStr;
+ SfxUndoManager::EnableUndo(bEnable);
}
- else
- GetRedoIds( &aTmpStr, pRedoIds );
-
-
- return aTmpStr;
}
-
-SwUndoId SwDoc::GetRedoIds( String* pStr, SwUndoIds *pRedoIds ) const
+void UndoManager::AddUndoAction(SfxUndoAction *pAction, sal_Bool bTryMerge)
{
- sal_uInt16 nTmpPos = nUndoPos;
- SwUndoId nId = UNDO_EMPTY;
-
- while (nTmpPos < pUndos->Count())
+ SwUndo *const pUndo( dynamic_cast<SwUndo *>(pAction) );
+ if (pUndo)
{
- SwUndo * pUndo = (*pUndos)[nTmpPos];
-
- SwUndoIdAndName * pIdAndName = lcl_GetUndoIdAndName(*pUndos, nTmpPos);
-
- if (nTmpPos == nUndoPos)
+ if (nsRedlineMode_t::REDLINE_NONE == pUndo->GetRedlineMode())
{
- nId = pIdAndName->GetUndoId();
-
- if (pStr)
- *pStr = *pIdAndName->GetUndoStr();
+ pUndo->SetRedlineMode( m_rRedlineAccess.GetRedlineMode() );
}
-
- if (pRedoIds)
- pRedoIds->Insert(pIdAndName, pRedoIds->Count());
- else
- break;
-
- if (pUndo->GetId() == UNDO_START)
- nTmpPos = nTmpPos + ((SwUndoStart *) pUndo)->GetEndOffset();
-
- nTmpPos++;
}
-
- return nId;
+ SfxUndoManager::AddUndoAction(pAction, bTryMerge);
+ // if the undo nodes array is too large, delete some actions
+ while (UNDO_ACTION_LIMIT < GetUndoNodes().Count())
+ {
+ RemoveOldestUndoActions(1);
+ }
}
-/**************** REPEAT ******************/
-
-
-bool SwDoc::Repeat( SwUndoIter& rUndoIter, sal_uInt16 nRepeatCnt )
+class CursorGuard
{
- if( rUndoIter.GetId() && !HasUndoId( rUndoIter.GetId() ) )
+public:
+ CursorGuard(SwEditShell & rShell, bool const bSave)
+ : m_rShell(rShell)
+ , m_bSaveCursor(bSave)
{
- rUndoIter.bWeiter = FALSE;
- return FALSE;
+ if (m_bSaveCursor)
+ {
+ m_rShell.Push(); // prevent modification of current cursor
+ }
}
- USHORT nSize = nUndoPos;
- if( !nSize )
+ ~CursorGuard()
{
- rUndoIter.bWeiter = FALSE;
- return FALSE;
+ if (m_bSaveCursor)
+ {
+ m_rShell.Pop();
+ }
}
+private:
+ SwEditShell & m_rShell;
+ bool const m_bSaveCursor;
+};
- // dann suche jetzt ueber die End/Start-Gruppen die gueltige Repeat-Aktion
- SwUndo *pUndo = (*pUndos)[ --nSize ];
- if( UNDO_END == pUndo->GetId() )
- nSize = nSize - ((SwUndoEnd*)pUndo)->GetSttOffset();
+bool UndoManager::impl_DoUndoRedo(UndoOrRedo_t const undoOrRedo)
+{
+ SwDoc & rDoc(*GetUndoNodes().GetDoc());
- USHORT nEndCnt = nUndoPos;
- BOOL bOneUndo = nSize + 1 == nUndoPos;
+ UnoActionContext c(& rDoc); // exception-safe StartAllAction/EndAllAction
- SwPaM* pTmpCrsr = rUndoIter.pAktPam;
- SwUndoId nId = UNDO_EMPTY;
+ SwEditShell *const pEditShell( rDoc.GetEditShell() );
- if( pTmpCrsr != pTmpCrsr->GetNext() || !bOneUndo ) // Undo-Klammerung aufbauen
+ OSL_ENSURE(pEditShell, "sw::UndoManager needs a SwEditShell!");
+ if (!pEditShell)
{
- if (pUndo->GetId() == UNDO_END)
- {
- SwUndoStart * pStartUndo =
- (SwUndoStart *) (*pUndos)[nSize];
-
- nId = pStartUndo->GetUserId();
- }
-
- StartUndo( nId, NULL );
+ throw uno::RuntimeException();
}
- do { // dann durchlaufe mal den gesamten Ring
- for( USHORT nRptCnt = nRepeatCnt; nRptCnt > 0; --nRptCnt )
- {
- rUndoIter.pLastUndoObj = 0;
- for( USHORT nCnt = nSize; nCnt < nEndCnt; ++nCnt )
- (*pUndos)[ nCnt ]->Repeat( rUndoIter ); // Repeat ausfuehren
- }
- } while( pTmpCrsr !=
- ( rUndoIter.pAktPam = (SwPaM*)rUndoIter.pAktPam->GetNext() ));
- if( pTmpCrsr != pTmpCrsr->GetNext() || !bOneUndo )
- EndUndo( nId, NULL );
- return TRUE;
-}
+ // in case the model has controllers locked, the Undo should not
+ // change the view cursors!
+ bool const bSaveCursors(pEditShell->CursorsLocked());
+ CursorGuard(*pEditShell, bSaveCursors);
+ if (!bSaveCursors)
+ {
+ // (in case Undo was called via API) clear the cursors:
+ pEditShell->KillPams();
+ pEditShell->SetMark();
+ pEditShell->ClearMark();
+ }
-// liefert die Id der letzten Repeatfaehigen Aktion zurueck oder 0
-// fuellt ggf. VARARR mit User-RedoIds
+ bool bRet(false);
-String SwDoc::GetRepeatIdsStr(String* pStr, SwUndoIds *pRepeatIds) const
-{
- String aTmpStr;
- SwUndoId nId;
+ ::sw::UndoRedoContext context(rDoc, *pEditShell);
- if ( pStr != NULL)
+ // N.B. these may throw!
+ if (UNDO == undoOrRedo)
{
- nId = GetRepeatIds(pStr, pRepeatIds);
- aTmpStr = *pStr;
+ bRet = SfxUndoManager::UndoWithContext(context);
}
else
- nId = GetRepeatIds(&aTmpStr, pRepeatIds);
+ {
+ bRet = SfxUndoManager::RedoWithContext(context);
+ }
- if (nId <= UNDO_END)
- return String();
+ if (bRet)
+ {
+ // if we are at the "last save" position, the document is not modified
+ if (SfxUndoManager::HasTopUndoActionMark(m_UndoSaveMark))
+ {
+ m_rState.ResetModified();
+ }
+ else
+ {
+ m_rState.SetModified();
+ }
+ }
+
+ pEditShell->HandleUndoRedoContext(context);
- return aTmpStr;
+ return bRet;
}
-SwUndoId SwDoc::GetRepeatIds(String* pStr, SwUndoIds *pRepeatIds) const
+sal_Bool UndoManager::Undo()
{
- SwUndoId nRepeatId = GetUndoIds( pStr, pRepeatIds );
- if( REPEAT_START <= nRepeatId && REPEAT_END > nRepeatId )
- return nRepeatId;
- return UNDO_EMPTY;
+ bool const bRet = impl_DoUndoRedo(UNDO);
+ return bRet;
}
+sal_Bool UndoManager::Redo()
+{
+ bool const bRet = impl_DoUndoRedo(REDO);
+ return bRet;
+}
-SwUndo* SwDoc::RemoveLastUndo( SwUndoId eUndoId )
+/** N.B.: this does _not_ call SfxUndoManager::Repeat because it is not
+ possible to wrap a list action around it:
+ calling EnterListAction here will cause SfxUndoManager::Repeat
+ to repeat the list action!
+ */
+bool
+UndoManager::Repeat(::sw::RepeatContext & rContext,
+ sal_uInt16 const nRepeatCount)
{
- SwUndo* pUndo = (*pUndos)[ nUndoPos - 1 ];
- if( eUndoId == pUndo->GetId() && nUndoPos == pUndos->Count() )
+ if (SfxUndoManager::IsInListAction())
{
- if( !nUndoSttEnd )
- --nUndoCnt;
- --nUndoPos;
- pUndos->Remove( nUndoPos, 1 );
+ OSL_ENSURE(false, "repeat in open list action???");
+ return false;
}
- else
+ if (!SfxUndoManager::GetUndoActionCount(TopLevel))
{
- pUndo = 0;
- ASSERT( !this, "falsches Undo-Object" );
+ return false;
+ }
+ SfxUndoAction *const pRepeatAction(GetUndoAction(0));
+ OSL_ASSERT(pRepeatAction);
+ if (!pRepeatAction || !pRepeatAction->CanRepeat(rContext))
+ {
+ return false;
}
- return pUndo;
-}
-SwUndoIdAndName::SwUndoIdAndName( SwUndoId nId, const String* pStr )
- : eUndoId( nId ), pUndoStr( pStr ? new String( *pStr ) : 0 )
-{
-}
+ ::rtl::OUString const comment(pRepeatAction->GetComment());
+ ::rtl::OUString const rcomment(pRepeatAction->GetRepeatComment(rContext));
+ USHORT const nId(pRepeatAction->GetId());
+ if (DoesUndo())
+ {
+ EnterListAction(comment, rcomment, nId);
+ }
-SwUndoIdAndName::~SwUndoIdAndName()
-{
- delete pUndoStr;
-}
+ SwPaM *const pFirstCursor(& rContext.GetRepeatPaM());
+ do { // iterate over ring
+ for (USHORT nRptCnt = nRepeatCount; nRptCnt > 0; --nRptCnt)
+ {
+ pRepeatAction->Repeat(rContext);
+ }
+ rContext.m_bDeleteRepeated = false; // reset for next PaM
+ rContext.m_pCurrentPaM =
+ static_cast<SwPaM*>(rContext.m_pCurrentPaM->GetNext());
+ } while (pFirstCursor != & rContext.GetRepeatPaM());
+ if (DoesUndo())
+ {
+ LeaveListAction();
+ }
+ return true;
+}
+} // namespace sw