diff options
Diffstat (limited to 'sw/source/core/undo/docundo.cxx')
-rw-r--r-- | sw/source/core/undo/docundo.cxx | 1133 |
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 |