LCOV - code coverage report
Current view: top level - svl/source/undo - undo.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 492 656 75.0 %
Date: 2015-06-13 12:38:46 Functions: 110 163 67.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include <svl/undo.hxx>
      21             : 
      22             : #include <com/sun/star/uno/Exception.hpp>
      23             : 
      24             : #include <osl/mutex.hxx>
      25             : #include <sal/log.hxx>
      26             : #include <comphelper/flagguard.hxx>
      27             : #include <tools/diagnose_ex.h>
      28             : #include <libxml/xmlwriter.h>
      29             : 
      30             : #include <vector>
      31             : #include <list>
      32             : #include <limits>
      33             : 
      34             : using ::com::sun::star::uno::Exception;
      35             : 
      36           0 : TYPEINIT0(SfxUndoAction);
      37           0 : TYPEINIT0(SfxListUndoAction);
      38           0 : TYPEINIT0(SfxLinkUndoAction);
      39          40 : TYPEINIT0(SfxRepeatTarget);
      40             : 
      41             : 
      42        9783 : SfxRepeatTarget::~SfxRepeatTarget()
      43             : {
      44        9783 : }
      45             : 
      46             : 
      47          38 : SfxUndoContext::~SfxUndoContext()
      48             : {
      49          38 : }
      50             : 
      51             : 
      52           0 : void SfxUndoAction::SetLinkToSfxLinkUndoAction(SfxLinkUndoAction* pSfxLinkUndoAction)
      53             : {
      54           0 :     mpSfxLinkUndoAction = pSfxLinkUndoAction;
      55           0 : }
      56             : 
      57             : 
      58      323109 : SfxUndoAction::~SfxUndoAction()
      59             : {
      60      323109 :     if(mpSfxLinkUndoAction)
      61             :     {
      62           0 :         mpSfxLinkUndoAction->LinkedSfxUndoActionDestructed(*this);
      63           0 :         mpSfxLinkUndoAction = 0;
      64             :     }
      65      323109 : }
      66             : 
      67             : 
      68      323827 : SfxUndoAction::SfxUndoAction()
      69      323827 : :   mpSfxLinkUndoAction(0)
      70             : {
      71      323827 : }
      72             : 
      73             : 
      74           0 : bool SfxUndoAction::Merge( SfxUndoAction * )
      75             : {
      76           0 :     return false;
      77             : }
      78             : 
      79             : 
      80         434 : OUString SfxUndoAction::GetComment() const
      81             : {
      82         434 :     return OUString();
      83             : }
      84             : 
      85             : 
      86             : 
      87           3 : sal_uInt16 SfxUndoAction::GetId() const
      88             : {
      89           3 :     return 0;
      90             : }
      91             : 
      92             : 
      93          50 : OUString SfxUndoAction::GetRepeatComment(SfxRepeatTarget&) const
      94             : {
      95          50 :     return GetComment();
      96             : }
      97             : 
      98             : 
      99           0 : void SfxUndoAction::Undo()
     100             : {
     101             :     // These are only conceptually pure virtual
     102             :     assert(!"pure virtual function called: SfxUndoAction::Undo()");
     103           0 : }
     104             : 
     105             : 
     106          25 : void SfxUndoAction::UndoWithContext( SfxUndoContext& i_context )
     107             : {
     108             :     (void)i_context;
     109          25 :     Undo();
     110          23 : }
     111             : 
     112             : 
     113           0 : void SfxUndoAction::Redo()
     114             : {
     115             :     // These are only conceptually pure virtual
     116             :     assert(!"pure virtual function called: SfxUndoAction::Redo()");
     117           0 : }
     118             : 
     119             : 
     120           2 : void SfxUndoAction::RedoWithContext( SfxUndoContext& i_context )
     121             : {
     122             :     (void)i_context;
     123           2 :     Redo();
     124           0 : }
     125             : 
     126             : 
     127           0 : void SfxUndoAction::Repeat(SfxRepeatTarget&)
     128             : {
     129             :     // These are only conceptually pure virtual
     130             :     assert(!"pure virtual function called: SfxUndoAction::Repeat()");
     131           0 : }
     132             : 
     133             : 
     134             : 
     135           0 : bool SfxUndoAction::CanRepeat(SfxRepeatTarget&) const
     136             : {
     137           0 :     return true;
     138             : }
     139             : 
     140           0 : void SfxUndoAction::dumpAsXml(xmlTextWriterPtr pWriter) const
     141             : {
     142           0 :     xmlTextWriterStartElement(pWriter, BAD_CAST("sfxUndoAction"));
     143           0 :     xmlTextWriterWriteAttribute(pWriter, BAD_CAST("symbol"), BAD_CAST(typeid(*this).name()));
     144           0 :     xmlTextWriterWriteAttribute(pWriter, BAD_CAST("comment"), BAD_CAST(GetComment().toUtf8().getStr()));
     145           0 :     xmlTextWriterEndElement(pWriter);
     146           0 : }
     147             : 
     148     3307555 : struct MarkedUndoAction
     149             : {
     150             :     SfxUndoAction*                  pAction;
     151             :     ::std::vector< UndoStackMark >  aMarks;
     152             : 
     153      317108 :     explicit MarkedUndoAction( SfxUndoAction* i_action )
     154             :         :pAction( i_action )
     155      317108 :         ,aMarks()
     156             :     {
     157      317108 :     }
     158             : };
     159             : 
     160      448368 : struct SfxUndoActions::Impl
     161             : {
     162             :     std::vector<MarkedUndoAction> maActions;
     163             : };
     164             : 
     165      224480 : SfxUndoActions::SfxUndoActions() : mpImpl(new Impl) {}
     166             : 
     167           0 : SfxUndoActions::SfxUndoActions( const SfxUndoActions& r ) :
     168           0 :     mpImpl(new Impl)
     169             : {
     170           0 :     mpImpl->maActions = r.mpImpl->maActions;
     171           0 : }
     172             : 
     173      223888 : SfxUndoActions::~SfxUndoActions()
     174             : {
     175      223888 :     delete mpImpl;
     176      223888 : }
     177             : 
     178     3026073 : bool SfxUndoActions::empty() const
     179             : {
     180     3026073 :     return mpImpl->maActions.empty();
     181             : }
     182             : 
     183      744620 : size_t SfxUndoActions::size() const
     184             : {
     185      744620 :     return mpImpl->maActions.size();
     186             : }
     187             : 
     188         454 : const MarkedUndoAction& SfxUndoActions::operator[]( size_t i ) const
     189             : {
     190         454 :     return mpImpl->maActions[i];
     191             : }
     192             : 
     193      474948 : MarkedUndoAction& SfxUndoActions::operator[]( size_t i )
     194             : {
     195      474948 :     return mpImpl->maActions[i];
     196             : }
     197             : 
     198           0 : const SfxUndoAction* SfxUndoActions::GetUndoAction( size_t i ) const
     199             : {
     200           0 :     return mpImpl->maActions[i].pAction;
     201             : }
     202             : 
     203           0 : SfxUndoAction* SfxUndoActions::GetUndoAction( size_t i )
     204             : {
     205           0 :     return mpImpl->maActions[i].pAction;
     206             : }
     207             : 
     208      316448 : void SfxUndoActions::Remove( size_t i_pos )
     209             : {
     210      316448 :     mpImpl->maActions.erase( mpImpl->maActions.begin() + i_pos );
     211      316448 : }
     212             : 
     213           2 : void SfxUndoActions::Remove( size_t i_pos, size_t i_count )
     214             : {
     215             :     mpImpl->maActions.erase(
     216           2 :         mpImpl->maActions.begin() + i_pos, mpImpl->maActions.begin() + i_pos + i_count);
     217           2 : }
     218             : 
     219      317108 : void SfxUndoActions::Insert( SfxUndoAction* i_action, size_t i_pos )
     220             : {
     221             :     mpImpl->maActions.insert(
     222      317108 :         mpImpl->maActions.begin() + i_pos, MarkedUndoAction( i_action ) );
     223      317108 : }
     224             : 
     225             : typedef ::std::vector< SfxUndoListener* >   UndoListeners;
     226             : 
     227             : struct SVL_DLLPRIVATE SfxUndoManager_Data
     228             : {
     229             :     ::osl::Mutex    aMutex;
     230             :     SfxUndoArray*   pUndoArray;
     231             :     SfxUndoArray*   pActUndoArray;
     232             :     SfxUndoArray*   pFatherUndoArray;
     233             : 
     234             :     sal_Int32       mnMarks;
     235             :     sal_Int32       mnEmptyMark;
     236             :     bool            mbUndoEnabled;
     237             :     bool            mbDoing;
     238             :     bool            mbClearUntilTopLevel;
     239             : 
     240             :     UndoListeners   aListeners;
     241             : 
     242       35728 :     explicit SfxUndoManager_Data( size_t i_nMaxUndoActionCount )
     243       35728 :         :pUndoArray( new SfxUndoArray( i_nMaxUndoActionCount ) )
     244             :         ,pActUndoArray( NULL )
     245             :         ,pFatherUndoArray( NULL )
     246             :         ,mnMarks( 0 )
     247       35728 :         ,mnEmptyMark(MARK_INVALID)
     248             :         ,mbUndoEnabled( true )
     249             :         ,mbDoing( false )
     250      107184 :         ,mbClearUntilTopLevel( false )
     251             :     {
     252       35728 :         pActUndoArray = pUndoArray;
     253       35728 :     }
     254             : 
     255       35423 :     ~SfxUndoManager_Data()
     256       35423 :     {
     257       35423 :         delete pUndoArray;
     258       35423 :     }
     259             : };
     260             : 
     261             : 
     262             : namespace svl { namespace undo { namespace impl
     263             : {
     264             :     class SVL_DLLPRIVATE LockGuard
     265             :     {
     266             :     public:
     267         300 :         explicit LockGuard( SfxUndoManager& i_manager )
     268         300 :             :m_manager( i_manager )
     269             :         {
     270         300 :             m_manager.ImplEnableUndo_Lock( false );
     271         300 :         }
     272             : 
     273         300 :         ~LockGuard()
     274             :         {
     275         300 :             m_manager.ImplEnableUndo_Lock( true );
     276         300 :         }
     277             : 
     278             :     private:
     279             :         SfxUndoManager& m_manager;
     280             :     };
     281             : 
     282             :     typedef void ( SfxUndoListener::*UndoListenerVoidMethod )();
     283             :     typedef void ( SfxUndoListener::*UndoListenerStringMethod )( const OUString& );
     284             : 
     285    22184312 :     struct SVL_DLLPRIVATE NotifyUndoListener : public ::std::unary_function< SfxUndoListener*, void >
     286             :     {
     287     2801643 :         explicit NotifyUndoListener( UndoListenerVoidMethod i_notificationMethod )
     288             :             :m_notificationMethod( i_notificationMethod )
     289             :             ,m_altNotificationMethod( NULL )
     290     2801643 :             ,m_sActionComment()
     291             :         {
     292     2801643 :         }
     293             : 
     294      387793 :         NotifyUndoListener( UndoListenerStringMethod i_notificationMethod, const OUString& i_actionComment )
     295             :             :m_notificationMethod( NULL )
     296             :             ,m_altNotificationMethod( i_notificationMethod )
     297      387793 :             ,m_sActionComment( i_actionComment )
     298             :         {
     299      387793 :         }
     300             : 
     301     3154001 :         bool is() const
     302             :         {
     303     3154001 :             return ( m_notificationMethod != 0 ) || ( m_altNotificationMethod != 0 );
     304             :         }
     305             : 
     306       32207 :         void operator()( SfxUndoListener* i_listener ) const
     307             :         {
     308             :             assert( is() && "NotifyUndoListener: this will crash!" );
     309       32207 :             if ( m_altNotificationMethod != 0 )
     310             :             {
     311       25892 :                 ( i_listener->*m_altNotificationMethod )( m_sActionComment );
     312             :             }
     313             :             else
     314             :             {
     315        6315 :                 ( i_listener->*m_notificationMethod )();
     316             :             }
     317       32207 :         }
     318             : 
     319             :     private:
     320             :         UndoListenerVoidMethod      m_notificationMethod;
     321             :         UndoListenerStringMethod    m_altNotificationMethod;
     322             :         OUString                    m_sActionComment;
     323             :     };
     324             : 
     325             :     class SVL_DLLPRIVATE UndoManagerGuard
     326             :     {
     327             :     public:
     328     5745197 :         explicit UndoManagerGuard( SfxUndoManager_Data& i_managerData )
     329             :             :m_rManagerData( i_managerData )
     330             :             ,m_aGuard( i_managerData.aMutex )
     331     5745197 :             ,m_notifiers()
     332             :         {
     333     5745199 :         }
     334             : 
     335             :         ~UndoManagerGuard();
     336             : 
     337         300 :         void clear()
     338             :         {
     339         300 :             m_aGuard.clear();
     340         300 :         }
     341             : 
     342         300 :         void reset()
     343             :         {
     344         300 :             m_aGuard.reset();
     345         300 :         }
     346             : 
     347          64 :         void cancelNotifications()
     348             :         {
     349          64 :             m_notifiers.clear();
     350          64 :         }
     351             : 
     352             :         /** marks the given Undo action for deletion
     353             : 
     354             :             The Undo action will be put into a list, whose members will be deleted from within the destructor of the
     355             :             UndoManagerGuard. This deletion will happen without the UndoManager's mutex locked.
     356             :         */
     357      216503 :         void    markForDeletion( SfxUndoAction* i_action )
     358             :         {
     359             :             // remember
     360      216503 :             if ( i_action )
     361      216503 :                 m_aUndoActionsCleanup.push_back( i_action );
     362      216503 :         }
     363             : 
     364             :         /** schedules the given SfxUndoListener method to be called for all registered listeners.
     365             : 
     366             :             The notification will happen after the Undo manager's mutex has been released, and after all pending
     367             :             deletions of Undo actions are done.
     368             :         */
     369     2766220 :         void    scheduleNotification( UndoListenerVoidMethod i_notificationMethod )
     370             :         {
     371     2766220 :             m_notifiers.push_back( NotifyUndoListener( i_notificationMethod ) );
     372     2766220 :         }
     373             : 
     374      387793 :         void    scheduleNotification( UndoListenerStringMethod i_notificationMethod, const OUString& i_actionComment )
     375             :         {
     376      387793 :             m_notifiers.push_back( NotifyUndoListener( i_notificationMethod, i_actionComment ) );
     377      387793 :         }
     378             : 
     379             :     private:
     380             :         SfxUndoManager_Data&                m_rManagerData;
     381             :         ::osl::ResettableMutexGuard         m_aGuard;
     382             :         ::std::list< SfxUndoAction* >       m_aUndoActionsCleanup;
     383             :         ::std::list< NotifyUndoListener >   m_notifiers;
     384             :     };
     385             : 
     386    11490398 :     UndoManagerGuard::~UndoManagerGuard()
     387             :     {
     388             :         // copy members
     389     5745199 :         UndoListeners aListenersCopy( m_rManagerData.aListeners );
     390             : 
     391             :         // release mutex
     392     5745199 :         m_aGuard.clear();
     393             : 
     394             :         // delete all actions
     395    11706901 :         while ( !m_aUndoActionsCleanup.empty() )
     396             :         {
     397      216503 :             SfxUndoAction* pAction = m_aUndoActionsCleanup.front();
     398      216503 :             m_aUndoActionsCleanup.pop_front();
     399             :             try
     400             :             {
     401      216503 :                 delete pAction;
     402             :             }
     403             :             catch( const Exception& )
     404             :             {
     405             :                 DBG_UNHANDLED_EXCEPTION();
     406             :             }
     407             :         }
     408             : 
     409             :         // handle scheduled notification
     410    26697600 :         for (   ::std::list< NotifyUndoListener >::const_iterator notifier = m_notifiers.begin();
     411    17798400 :                 notifier != m_notifiers.end();
     412             :                 ++notifier
     413             :              )
     414             :         {
     415     3154001 :             if ( notifier->is() )
     416     3154001 :                 ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(), *notifier );
     417     5745199 :         }
     418     5745199 :     }
     419             : } } }
     420             : 
     421             : using namespace ::svl::undo::impl;
     422             : 
     423             : 
     424       35728 : SfxUndoManager::SfxUndoManager( size_t nMaxUndoActionCount )
     425       35728 :     :m_xData( new SfxUndoManager_Data( nMaxUndoActionCount ) )
     426             : {
     427       35728 : }
     428             : 
     429             : 
     430       70846 : SfxUndoManager::~SfxUndoManager()
     431             : {
     432       35423 :     UndoListeners aListenersCopy;
     433             :     {
     434       35423 :         UndoManagerGuard aGuard( *m_xData );
     435       35423 :         aListenersCopy = m_xData->aListeners;
     436             :     }
     437             : 
     438             :     ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(),
     439       35423 :         NotifyUndoListener( &SfxUndoListener::undoManagerDying ) );
     440       35423 : }
     441             : 
     442             : 
     443      111079 : void SfxUndoManager::EnableUndo( bool i_enable )
     444             : {
     445      111079 :     UndoManagerGuard aGuard( *m_xData );
     446      111079 :     ImplEnableUndo_Lock( i_enable );
     447             : 
     448      111079 : }
     449             : 
     450             : 
     451      111679 : void SfxUndoManager::ImplEnableUndo_Lock( bool const i_enable )
     452             : {
     453      111679 :     if ( m_xData->mbUndoEnabled == i_enable )
     454      193085 :         return;
     455       30273 :     m_xData->mbUndoEnabled = i_enable;
     456             : }
     457             : 
     458             : 
     459     2318713 : bool SfxUndoManager::IsUndoEnabled() const
     460             : {
     461     2318713 :     UndoManagerGuard aGuard( *m_xData );
     462     2318713 :     return ImplIsUndoEnabled_Lock();
     463             : }
     464             : 
     465             : 
     466     3013384 : bool SfxUndoManager::ImplIsUndoEnabled_Lock() const
     467             : {
     468     3013384 :     return m_xData->mbUndoEnabled;
     469             : }
     470             : 
     471             : 
     472        7523 : void SfxUndoManager::SetMaxUndoActionCount( size_t nMaxUndoActionCount )
     473             : {
     474        7523 :     UndoManagerGuard aGuard( *m_xData );
     475             : 
     476             :     // Remove entries from the pActUndoArray when we have to reduce
     477             :     // the number of entries due to a lower nMaxUndoActionCount.
     478             :     // Both redo and undo action entries will be removed until we reached the
     479             :     // new nMaxUndoActionCount.
     480             : 
     481        7523 :     long nNumToDelete = m_xData->pActUndoArray->aUndoActions.size() - nMaxUndoActionCount;
     482       15054 :     while ( nNumToDelete > 0 )
     483             :     {
     484           8 :         size_t nPos = m_xData->pActUndoArray->aUndoActions.size();
     485           8 :         if ( nPos > m_xData->pActUndoArray->nCurUndoAction )
     486             :         {
     487           0 :             SfxUndoAction* pAction = m_xData->pActUndoArray->aUndoActions[nPos-1].pAction;
     488           0 :             aGuard.markForDeletion( pAction );
     489           0 :             m_xData->pActUndoArray->aUndoActions.Remove( nPos-1 );
     490           0 :             --nNumToDelete;
     491             :         }
     492             : 
     493           8 :         if ( nNumToDelete > 0 && m_xData->pActUndoArray->nCurUndoAction > 0 )
     494             :         {
     495           8 :             SfxUndoAction* pAction = m_xData->pActUndoArray->aUndoActions[0].pAction;
     496           8 :             aGuard.markForDeletion( pAction );
     497           8 :             m_xData->pActUndoArray->aUndoActions.Remove(0);
     498           8 :             --m_xData->pActUndoArray->nCurUndoAction;
     499           8 :             --nNumToDelete;
     500             :         }
     501             : 
     502           8 :         if ( nPos == m_xData->pActUndoArray->aUndoActions.size() )
     503           0 :             break; // Cannot delete more entries
     504             :     }
     505             : 
     506        7523 :     m_xData->pActUndoArray->nMaxUndoActions = nMaxUndoActionCount;
     507        7523 : }
     508             : 
     509             : 
     510           0 : size_t SfxUndoManager::GetMaxUndoActionCount() const
     511             : {
     512           0 :     UndoManagerGuard aGuard( *m_xData );
     513           0 :     return m_xData->pActUndoArray->nMaxUndoActions;
     514             : }
     515             : 
     516             : 
     517     2629510 : void SfxUndoManager::ImplClearCurrentLevel_NoNotify( UndoManagerGuard& i_guard )
     518             : {
     519             :     // clear array
     520     5331554 :     while ( !m_xData->pActUndoArray->aUndoActions.empty() )
     521             :     {
     522       72534 :         size_t deletePos = m_xData->pActUndoArray->aUndoActions.size() - 1;
     523       72534 :         SfxUndoAction* pAction = m_xData->pActUndoArray->aUndoActions[ deletePos ].pAction;
     524       72534 :         i_guard.markForDeletion( pAction );
     525       72534 :         m_xData->pActUndoArray->aUndoActions.Remove( deletePos );
     526             :     }
     527             : 
     528     2629510 :     m_xData->pActUndoArray->nCurUndoAction = 0;
     529             : 
     530     2629510 :     m_xData->mnMarks = 0;
     531     2629510 :     m_xData->mnEmptyMark = MARK_INVALID;
     532     2629510 : }
     533             : 
     534             : 
     535     2626205 : void SfxUndoManager::Clear()
     536             : {
     537     2626205 :     UndoManagerGuard aGuard( *m_xData );
     538             : 
     539             :     SAL_WARN_IF( ImplIsInListAction_Lock(), "svl",
     540             :         "SfxUndoManager::Clear: suspicious call - do you really wish to clear the current level?" );
     541     2626205 :     ImplClearCurrentLevel_NoNotify( aGuard );
     542             : 
     543             :     // notify listeners
     544     2626205 :     aGuard.scheduleNotification( &SfxUndoListener::cleared );
     545     2626205 : }
     546             : 
     547             : 
     548        3241 : void SfxUndoManager::ClearAllLevels()
     549             : {
     550        3241 :     UndoManagerGuard aGuard( *m_xData );
     551        3241 :     ImplClearCurrentLevel_NoNotify( aGuard );
     552             : 
     553        3241 :     if ( ImplIsInListAction_Lock() )
     554             :     {
     555           0 :         m_xData->mbClearUntilTopLevel = true;
     556             :     }
     557             :     else
     558             :     {
     559        3241 :         aGuard.scheduleNotification( &SfxUndoListener::cleared );
     560        3241 :     }
     561        3241 : }
     562             : 
     563             : 
     564       18382 : void SfxUndoManager::ImplClearRedo_NoLock( bool const i_currentLevel )
     565             : {
     566       18382 :     UndoManagerGuard aGuard( *m_xData );
     567       18382 :     ImplClearRedo( aGuard, i_currentLevel );
     568       18382 : }
     569             : 
     570             : 
     571          14 : void SfxUndoManager::ClearRedo()
     572             : {
     573             :     SAL_WARN_IF( IsInListAction(), "svl",
     574             :         "SfxUndoManager::ClearRedo: suspicious call - do you really wish to clear the current level?" );
     575          14 :     ImplClearRedo_NoLock( CurrentLevel );
     576          14 : }
     577             : 
     578             : 
     579          64 : void SfxUndoManager::Reset()
     580             : {
     581          64 :     UndoManagerGuard aGuard( *m_xData );
     582             : 
     583             :     // clear all locks
     584         128 :     while ( !ImplIsUndoEnabled_Lock() )
     585           0 :         ImplEnableUndo_Lock( true );
     586             : 
     587             :     // cancel all list actions
     588         140 :     while ( IsInListAction() )
     589          12 :         ImplLeaveListAction( false, aGuard );
     590             : 
     591             :     // clear both stacks
     592          64 :     ImplClearCurrentLevel_NoNotify( aGuard );
     593             : 
     594             :     // cancel the notifications scheduled by ImplLeaveListAction,
     595             :     // as we want to do an own, dedicated notification
     596          64 :     aGuard.cancelNotifications();
     597             : 
     598             :     // schedule notification
     599          64 :     aGuard.scheduleNotification( &SfxUndoListener::resetAll );
     600          64 : }
     601             : 
     602             : 
     603           8 : void SfxUndoManager::ImplClearUndo( UndoManagerGuard& i_guard )
     604             : {
     605          24 :     while ( m_xData->pActUndoArray->nCurUndoAction > 0 )
     606             :     {
     607           8 :         SfxUndoAction* pUndoAction = m_xData->pActUndoArray->aUndoActions[0].pAction;
     608           8 :         m_xData->pActUndoArray->aUndoActions.Remove( 0 );
     609           8 :         i_guard.markForDeletion( pUndoAction );
     610           8 :         --m_xData->pActUndoArray->nCurUndoAction;
     611             :     }
     612             :     // TODO: notifications? We don't have clearedUndo, only cleared and clearedRedo at the SfxUndoListener
     613           8 : }
     614             : 
     615             : 
     616       88830 : void SfxUndoManager::ImplClearRedo( UndoManagerGuard& i_guard, bool const i_currentLevel )
     617             : {
     618       88830 :     SfxUndoArray* pUndoArray = ( i_currentLevel == IUndoManager::CurrentLevel ) ? m_xData->pActUndoArray : m_xData->pUndoArray;
     619             : 
     620             :     // clearance
     621      177713 :     while ( pUndoArray->aUndoActions.size() > pUndoArray->nCurUndoAction )
     622             :     {
     623          53 :         size_t deletePos = pUndoArray->aUndoActions.size() - 1;
     624          53 :         SfxUndoAction* pAction = pUndoArray->aUndoActions[ deletePos ].pAction;
     625          53 :         pUndoArray->aUndoActions.Remove( deletePos );
     626          53 :         i_guard.markForDeletion( pAction );
     627             :     }
     628             : 
     629             :     // notification - only if the top level's stack was cleared
     630       88830 :     if ( i_currentLevel == IUndoManager::TopLevel )
     631       18368 :         i_guard.scheduleNotification( &SfxUndoListener::clearedRedo );
     632       88830 : }
     633             : 
     634             : 
     635      317104 : bool SfxUndoManager::ImplAddUndoAction_NoNotify( SfxUndoAction *pAction, bool bTryMerge, bool bClearRedo, UndoManagerGuard& i_guard )
     636             : {
     637      317104 :     if ( !ImplIsUndoEnabled_Lock() || ( m_xData->pActUndoArray->nMaxUndoActions == 0 ) )
     638             :     {
     639           0 :         i_guard.markForDeletion( pAction );
     640           0 :         return false;
     641             :     }
     642             : 
     643             :     // merge, if required
     644      317104 :     SfxUndoAction* pMergeWithAction = m_xData->pActUndoArray->nCurUndoAction ?
     645      317104 :         m_xData->pActUndoArray->aUndoActions[m_xData->pActUndoArray->nCurUndoAction-1].pAction : NULL;
     646      317104 :     if ( bTryMerge && pMergeWithAction )
     647             :     {
     648           4 :         bool bMerged = pMergeWithAction->Merge( pAction );
     649           4 :         if ( bMerged )
     650             :         {
     651           4 :             i_guard.markForDeletion( pAction );
     652           4 :             return false;
     653             :         }
     654             :     }
     655             : 
     656             :     // clear redo stack, if requested
     657      317100 :     if ( bClearRedo && ( ImplGetRedoActionCount_Lock( CurrentLevel ) > 0 ) )
     658          31 :         ImplClearRedo( i_guard, IUndoManager::CurrentLevel );
     659             : 
     660             :     // respect max number
     661      317100 :     if( m_xData->pActUndoArray == m_xData->pUndoArray )
     662             :     {
     663      460068 :         while(m_xData->pActUndoArray->aUndoActions.size() >= m_xData->pActUndoArray->nMaxUndoActions)
     664             :         {
     665       25552 :             i_guard.markForDeletion( m_xData->pActUndoArray->aUndoActions[0].pAction );
     666       25552 :             m_xData->pActUndoArray->aUndoActions.Remove(0);
     667       25552 :             if (m_xData->pActUndoArray->nCurUndoAction > 0)
     668             :             {
     669       25552 :                 --m_xData->pActUndoArray->nCurUndoAction;
     670             :             }
     671             :             else
     672             :             {
     673             :                 assert(!"CurrentUndoAction going negative (!)");
     674             :             }
     675             :             // fdo#66071 invalidate the current empty mark when removing
     676       25552 :             --m_xData->mnEmptyMark;
     677             :         }
     678             :     }
     679             : 
     680             :     // append new action
     681      317100 :     m_xData->pActUndoArray->aUndoActions.Insert( pAction, m_xData->pActUndoArray->nCurUndoAction++ );
     682      317100 :     return true;
     683             : }
     684             : 
     685             : 
     686      128352 : void SfxUndoManager::AddUndoAction( SfxUndoAction *pAction, bool bTryMerge )
     687             : {
     688      128352 :     UndoManagerGuard aGuard( *m_xData );
     689             : 
     690             :     // add
     691      128352 :     if ( ImplAddUndoAction_NoNotify( pAction, bTryMerge, true, aGuard ) )
     692             :     {
     693             :         // notify listeners
     694      128348 :         aGuard.scheduleNotification( &SfxUndoListener::undoActionAdded, pAction->GetComment() );
     695      128352 :     }
     696      128352 : }
     697             : 
     698             : 
     699       12275 : size_t SfxUndoManager::GetUndoActionCount( bool const i_currentLevel ) const
     700             : {
     701       12275 :     UndoManagerGuard aGuard( *m_xData );
     702       12275 :     const SfxUndoArray* pUndoArray = i_currentLevel ? m_xData->pActUndoArray : m_xData->pUndoArray;
     703       12275 :     return pUndoArray->nCurUndoAction;
     704             : }
     705             : 
     706             : 
     707         339 : OUString SfxUndoManager::GetUndoActionComment( size_t nNo, bool const i_currentLevel ) const
     708             : {
     709         339 :     UndoManagerGuard aGuard( *m_xData );
     710             : 
     711         339 :     OUString sComment;
     712         339 :     const SfxUndoArray* pUndoArray = i_currentLevel ? m_xData->pActUndoArray : m_xData->pUndoArray;
     713             :     assert(nNo < pUndoArray->nCurUndoAction);
     714         339 :     if( nNo < pUndoArray->nCurUndoAction )
     715         339 :         sComment = pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction - 1 - nNo ].pAction->GetComment();
     716         339 :     return sComment;
     717             : }
     718             : 
     719             : 
     720           0 : sal_uInt16 SfxUndoManager::GetUndoActionId() const
     721             : {
     722           0 :     UndoManagerGuard aGuard( *m_xData );
     723             : 
     724             :     assert(m_xData->pActUndoArray->nCurUndoAction > 0);
     725           0 :     if ( m_xData->pActUndoArray->nCurUndoAction == 0 )
     726           0 :         return 0;
     727           0 :     return m_xData->pActUndoArray->aUndoActions[m_xData->pActUndoArray->nCurUndoAction-1].pAction->GetId();
     728             : }
     729             : 
     730             : 
     731       15996 : SfxUndoAction* SfxUndoManager::GetUndoAction( size_t nNo ) const
     732             : {
     733       15996 :     UndoManagerGuard aGuard( *m_xData );
     734             : 
     735             :     assert(nNo < m_xData->pActUndoArray->nCurUndoAction);
     736       15996 :     if( nNo >= m_xData->pActUndoArray->nCurUndoAction )
     737           0 :         return NULL;
     738       15996 :     return m_xData->pActUndoArray->aUndoActions[m_xData->pActUndoArray->nCurUndoAction-1-nNo].pAction;
     739             : }
     740             : 
     741             : 
     742             : /** clears the redo stack and removes the top undo action */
     743           2 : void SfxUndoManager::RemoveLastUndoAction()
     744             : {
     745           2 :     UndoManagerGuard aGuard( *m_xData );
     746             : 
     747           4 :     ENSURE_OR_RETURN_VOID( m_xData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::RemoveLastUndoAction(), no action to remove?!" );
     748             : 
     749           2 :     m_xData->pActUndoArray->nCurUndoAction--;
     750             : 
     751             :     // delete redo-actions and top action
     752           4 :     for ( size_t nPos = m_xData->pActUndoArray->aUndoActions.size(); nPos > m_xData->pActUndoArray->nCurUndoAction; --nPos )
     753             :     {
     754           2 :         aGuard.markForDeletion( m_xData->pActUndoArray->aUndoActions[nPos-1].pAction );
     755             :     }
     756             : 
     757           2 :     m_xData->pActUndoArray->aUndoActions.Remove(
     758           2 :         m_xData->pActUndoArray->nCurUndoAction,
     759           6 :         m_xData->pActUndoArray->aUndoActions.size() - m_xData->pActUndoArray->nCurUndoAction );
     760             : }
     761             : 
     762             : 
     763        4618 : bool SfxUndoManager::IsDoing() const
     764             : {
     765        4618 :     UndoManagerGuard aGuard( *m_xData );
     766        4618 :     return m_xData->mbDoing;
     767             : }
     768             : 
     769             : 
     770         230 : bool SfxUndoManager::Undo()
     771             : {
     772         230 :     return ImplUndo( NULL );
     773             : }
     774             : 
     775             : 
     776          31 : bool SfxUndoManager::UndoWithContext( SfxUndoContext& i_context )
     777             : {
     778          31 :     return ImplUndo( &i_context );
     779             : }
     780             : 
     781             : 
     782         261 : bool SfxUndoManager::ImplUndo( SfxUndoContext* i_contextOrNull )
     783             : {
     784         261 :     UndoManagerGuard aGuard( *m_xData );
     785             :     assert( !IsDoing() && "SfxUndoManager::Undo: *nested* Undo/Redo actions? How this?" );
     786             : 
     787         522 :     ::comphelper::FlagGuard aDoingGuard( m_xData->mbDoing );
     788         522 :     LockGuard aLockGuard( *this );
     789             : 
     790         261 :     if ( ImplIsInListAction_Lock() )
     791             :     {
     792             :         assert(!"SfxUndoManager::Undo: not possible when within a list action!");
     793           0 :         return false;
     794             :     }
     795             : 
     796         261 :     if ( m_xData->pActUndoArray->nCurUndoAction == 0 )
     797             :     {
     798             :         SAL_WARN("svl", "SfxUndoManager::Undo: undo stack is empty!" );
     799           0 :         return false;
     800             :     }
     801             : 
     802         261 :     SfxUndoAction* pAction = m_xData->pActUndoArray->aUndoActions[ --m_xData->pActUndoArray->nCurUndoAction ].pAction;
     803         522 :     const OUString sActionComment = pAction->GetComment();
     804             :     try
     805             :     {
     806             :         // clear the guard/mutex before calling into the SfxUndoAction - this can be an extension-implemented UNO component
     807             :         // nowadays ...
     808         261 :         aGuard.clear();
     809         261 :         if ( i_contextOrNull != NULL )
     810          31 :             pAction->UndoWithContext( *i_contextOrNull );
     811             :         else
     812         230 :             pAction->Undo();
     813         253 :         aGuard.reset();
     814             :     }
     815           8 :     catch( ... )
     816             :     {
     817           8 :         aGuard.reset();
     818             : 
     819             :         // in theory, somebody might have tampered with all of *m_xData while the mutex was unlocked. So, see if
     820             :         // we still find pAction in our current Undo array
     821           8 :         size_t nCurAction = 0;
     822          24 :         while ( nCurAction < m_xData->pActUndoArray->aUndoActions.size() )
     823             :         {
     824          16 :             if ( m_xData->pActUndoArray->aUndoActions[ nCurAction++ ].pAction == pAction )
     825             :             {
     826             :                 // the Undo action is still there ...
     827             :                 // assume the error is a permanent failure, and clear the Undo stack
     828           8 :                 ImplClearUndo( aGuard );
     829           8 :                 throw;
     830             :             }
     831             :         }
     832             :         SAL_WARN("svl", "SfxUndoManager::Undo: can't clear the Undo stack after the failure - some other party was faster ..." );
     833           0 :         throw;
     834             :     }
     835             : 
     836         253 :     aGuard.scheduleNotification( &SfxUndoListener::actionUndone, sActionComment );
     837             : 
     838         514 :     return true;
     839             : }
     840             : 
     841             : 
     842        3130 : size_t SfxUndoManager::GetRedoActionCount( bool const i_currentLevel ) const
     843             : {
     844        3130 :     UndoManagerGuard aGuard( *m_xData );
     845        3130 :     return ImplGetRedoActionCount_Lock( i_currentLevel );
     846             : }
     847             : 
     848             : 
     849      131478 : size_t SfxUndoManager::ImplGetRedoActionCount_Lock( bool const i_currentLevel ) const
     850             : {
     851      131478 :     const SfxUndoArray* pUndoArray = i_currentLevel ? m_xData->pActUndoArray : m_xData->pUndoArray;
     852      131478 :     return pUndoArray->aUndoActions.size() - pUndoArray->nCurUndoAction;
     853             : }
     854             : 
     855             : 
     856          37 : SfxUndoAction* SfxUndoManager::GetRedoAction( size_t nNo, bool const i_currentLevel ) const
     857             : {
     858          37 :     UndoManagerGuard aGuard( *m_xData );
     859             : 
     860          37 :     const SfxUndoArray* pUndoArray = i_currentLevel ? m_xData->pActUndoArray : m_xData->pUndoArray;
     861          37 :     if ( (pUndoArray->nCurUndoAction + nNo) > pUndoArray->aUndoActions.size() )
     862             :     {
     863           0 :         return NULL;
     864             :     }
     865          37 :     return pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction;
     866             : }
     867             : 
     868             : 
     869          54 : OUString SfxUndoManager::GetRedoActionComment( size_t nNo, bool const i_currentLevel ) const
     870             : {
     871          54 :     OUString sComment;
     872         108 :     UndoManagerGuard aGuard( *m_xData );
     873          54 :     const SfxUndoArray* pUndoArray = i_currentLevel ? m_xData->pActUndoArray : m_xData->pUndoArray;
     874          54 :     if ( (pUndoArray->nCurUndoAction + nNo) < pUndoArray->aUndoActions.size() )
     875             :     {
     876          54 :         sComment = pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction->GetComment();
     877             :     }
     878         108 :     return sComment;
     879             : }
     880             : 
     881             : 
     882          33 : bool SfxUndoManager::Redo()
     883             : {
     884          33 :     return ImplRedo( NULL );
     885             : }
     886             : 
     887             : 
     888           6 : bool SfxUndoManager::RedoWithContext( SfxUndoContext& i_context )
     889             : {
     890           6 :     return ImplRedo( &i_context );
     891             : }
     892             : 
     893             : 
     894          39 : bool SfxUndoManager::ImplRedo( SfxUndoContext* i_contextOrNull )
     895             : {
     896          39 :     UndoManagerGuard aGuard( *m_xData );
     897             :     assert( !IsDoing() && "SfxUndoManager::Redo: *nested* Undo/Redo actions? How this?" );
     898             : 
     899          78 :     ::comphelper::FlagGuard aDoingGuard( m_xData->mbDoing );
     900          78 :     LockGuard aLockGuard( *this );
     901             : 
     902          39 :     if ( ImplIsInListAction_Lock() )
     903             :     {
     904             :         assert(!"SfxUndoManager::Redo: not possible when within a list action!");
     905           0 :         return false;
     906             :     }
     907             : 
     908          39 :     if ( m_xData->pActUndoArray->nCurUndoAction >= m_xData->pActUndoArray->aUndoActions.size() )
     909             :     {
     910             :         SAL_WARN("svl", "SfxUndoManager::Redo: redo stack is empty!");
     911           0 :         return false;
     912             :     }
     913             : 
     914          39 :     SfxUndoAction* pAction = m_xData->pActUndoArray->aUndoActions[ m_xData->pActUndoArray->nCurUndoAction++ ].pAction;
     915          78 :     const OUString sActionComment = pAction->GetComment();
     916             :     try
     917             :     {
     918             :         // clear the guard/mutex before calling into the SfxUndoAction - this can be a extension-implemented UNO component
     919             :         // nowadays ...
     920          39 :         aGuard.clear();
     921          39 :         if ( i_contextOrNull != NULL )
     922           6 :             pAction->RedoWithContext( *i_contextOrNull );
     923             :         else
     924          33 :             pAction->Redo();
     925          31 :         aGuard.reset();
     926             :     }
     927           8 :     catch( ... )
     928             :     {
     929           8 :         aGuard.reset();
     930             : 
     931             :         // in theory, somebody might have tampered with all of *m_xData while the mutex was unlocked. So, see if
     932             :         // we still find pAction in our current Undo array
     933           8 :         size_t nCurAction = 0;
     934          24 :         while ( nCurAction < m_xData->pActUndoArray->aUndoActions.size() )
     935             :         {
     936          16 :             if ( m_xData->pActUndoArray->aUndoActions[ nCurAction ].pAction == pAction )
     937             :             {
     938             :                 // the Undo action is still there ...
     939             :                 // assume the error is a permanent failure, and clear the Undo stack
     940           8 :                 ImplClearRedo( aGuard, IUndoManager::CurrentLevel );
     941           8 :                 throw;
     942             :             }
     943           8 :             ++nCurAction;
     944             :         }
     945             :         SAL_WARN("svl", "SfxUndoManager::Redo: can't clear the Undo stack after the failure - some other party was faster ..." );
     946           0 :         throw;
     947             :     }
     948             : 
     949          31 :     aGuard.scheduleNotification( &SfxUndoListener::actionRedone, sActionComment );
     950             : 
     951          70 :     return true;
     952             : }
     953             : 
     954             : 
     955         950 : size_t SfxUndoManager::GetRepeatActionCount() const
     956             : {
     957         950 :     UndoManagerGuard aGuard( *m_xData );
     958         950 :     return m_xData->pActUndoArray->aUndoActions.size();
     959             : }
     960             : 
     961             : 
     962          70 : OUString SfxUndoManager::GetRepeatActionComment(SfxRepeatTarget &rTarget) const
     963             : {
     964          70 :     UndoManagerGuard aGuard( *m_xData );
     965          70 :     return m_xData->pActUndoArray->aUndoActions[ m_xData->pActUndoArray->aUndoActions.size() - 1 ].pAction
     966          70 :         ->GetRepeatComment(rTarget);
     967             : }
     968             : 
     969             : 
     970           0 : bool SfxUndoManager::Repeat( SfxRepeatTarget &rTarget )
     971             : {
     972           0 :     UndoManagerGuard aGuard( *m_xData );
     973           0 :     if ( !m_xData->pActUndoArray->aUndoActions.empty() )
     974             :     {
     975           0 :         SfxUndoAction* pAction = m_xData->pActUndoArray->aUndoActions[ m_xData->pActUndoArray->aUndoActions.size() - 1 ].pAction;
     976           0 :         aGuard.clear();
     977           0 :         if ( pAction->CanRepeat( rTarget ) )
     978           0 :             pAction->Repeat( rTarget );
     979           0 :         return true;
     980             :     }
     981             : 
     982           0 :     return false;
     983             : }
     984             : 
     985             : 
     986         198 : bool SfxUndoManager::CanRepeat( SfxRepeatTarget &rTarget ) const
     987             : {
     988         198 :     UndoManagerGuard aGuard( *m_xData );
     989         198 :     if ( !m_xData->pActUndoArray->aUndoActions.empty() )
     990             :     {
     991         198 :         size_t nActionNo = m_xData->pActUndoArray->aUndoActions.size() - 1;
     992         198 :         return m_xData->pActUndoArray->aUndoActions[nActionNo].pAction->CanRepeat(rTarget);
     993             :     }
     994           0 :     return false;
     995             : }
     996             : 
     997             : 
     998         391 : void SfxUndoManager::AddUndoListener( SfxUndoListener& i_listener )
     999             : {
    1000         391 :     UndoManagerGuard aGuard( *m_xData );
    1001         391 :     m_xData->aListeners.push_back( &i_listener );
    1002         391 : }
    1003             : 
    1004             : 
    1005         391 : void SfxUndoManager::RemoveUndoListener( SfxUndoListener& i_listener )
    1006             : {
    1007         391 :     UndoManagerGuard aGuard( *m_xData );
    1008        1173 :     for (   UndoListeners::iterator lookup = m_xData->aListeners.begin();
    1009         782 :             lookup != m_xData->aListeners.end();
    1010             :             ++lookup
    1011             :         )
    1012             :     {
    1013         391 :         if ( (*lookup) == &i_listener )
    1014             :         {
    1015         391 :             m_xData->aListeners.erase( lookup );
    1016         391 :             break;
    1017             :         }
    1018         391 :     }
    1019         391 : }
    1020             : 
    1021             : /**
    1022             :  * Inserts a ListUndoAction and sets its UndoArray as current.
    1023             :  */
    1024      188752 : void SfxUndoManager::EnterListAction( const OUString& rComment,
    1025             :                                       const OUString &rRepeatComment, sal_uInt16 nId )
    1026             : {
    1027      188752 :     UndoManagerGuard aGuard( *m_xData );
    1028             : 
    1029      188752 :     if( !ImplIsUndoEnabled_Lock() )
    1030           0 :         return;
    1031             : 
    1032      188752 :     if ( !m_xData->pUndoArray->nMaxUndoActions )
    1033           0 :         return;
    1034             : 
    1035      188752 :     m_xData->pFatherUndoArray = m_xData->pActUndoArray;
    1036      188752 :     SfxListUndoAction* pAction = new SfxListUndoAction( rComment, rRepeatComment, nId, m_xData->pActUndoArray );
    1037      188752 :     OSL_VERIFY( ImplAddUndoAction_NoNotify( pAction, false, false, aGuard ) );
    1038             :     // expected to succeed: all conditions under which it could fail should have been checked already
    1039      188752 :     m_xData->pActUndoArray = pAction;
    1040             : 
    1041             :     // notification
    1042      188752 :     aGuard.scheduleNotification( &SfxUndoListener::listActionEntered, rComment );
    1043             : }
    1044             : 
    1045             : 
    1046       15953 : bool SfxUndoManager::IsInListAction() const
    1047             : {
    1048       15953 :     UndoManagerGuard aGuard( *m_xData );
    1049       15953 :     return ImplIsInListAction_Lock();
    1050             : }
    1051             : 
    1052             : 
    1053      208245 : bool SfxUndoManager::ImplIsInListAction_Lock() const
    1054             : {
    1055      208245 :     return ( m_xData->pActUndoArray != m_xData->pUndoArray );
    1056             : }
    1057             : 
    1058             : 
    1059       26130 : size_t SfxUndoManager::GetListActionDepth() const
    1060             : {
    1061       26130 :     UndoManagerGuard aGuard( *m_xData );
    1062       26130 :     size_t nDepth(0);
    1063             : 
    1064       26130 :     SfxUndoArray* pLookup( m_xData->pActUndoArray );
    1065       61854 :     while ( pLookup != m_xData->pUndoArray )
    1066             :     {
    1067        9594 :         pLookup = pLookup->pFatherUndoArray;
    1068        9594 :         ++nDepth;
    1069             :     }
    1070             : 
    1071       26130 :     return nDepth;
    1072             : }
    1073             : 
    1074             : 
    1075      188723 : size_t SfxUndoManager::LeaveListAction()
    1076             : {
    1077      188723 :     UndoManagerGuard aGuard( *m_xData );
    1078      188723 :     size_t nCount = ImplLeaveListAction( false, aGuard );
    1079             : 
    1080      188723 :     if ( m_xData->mbClearUntilTopLevel )
    1081             :     {
    1082           0 :         ImplClearCurrentLevel_NoNotify( aGuard );
    1083           0 :         if ( !ImplIsInListAction_Lock() )
    1084             :         {
    1085           0 :             m_xData->mbClearUntilTopLevel = false;
    1086           0 :             aGuard.scheduleNotification( &SfxUndoListener::cleared );
    1087             :         }
    1088           0 :         nCount = 0;
    1089             :     }
    1090             : 
    1091      188723 :     return nCount;
    1092             : }
    1093             : 
    1094             : 
    1095          16 : size_t SfxUndoManager::LeaveAndMergeListAction()
    1096             : {
    1097          16 :     UndoManagerGuard aGuard( *m_xData );
    1098          16 :     return ImplLeaveListAction( true, aGuard );
    1099             : }
    1100             : 
    1101             : 
    1102      188751 : size_t SfxUndoManager::ImplLeaveListAction( const bool i_merge, UndoManagerGuard& i_guard )
    1103             : {
    1104      188751 :     if ( !ImplIsUndoEnabled_Lock() )
    1105           0 :         return 0;
    1106             : 
    1107      188751 :     if ( !m_xData->pUndoArray->nMaxUndoActions )
    1108           0 :         return 0;
    1109             : 
    1110      188751 :     if( !ImplIsInListAction_Lock() )
    1111             :     {
    1112             :         SAL_WARN("svl", "svl::SfxUndoManager::ImplLeaveListAction, called without calling EnterListAction()!" );
    1113           0 :         return 0;
    1114             :     }
    1115             : 
    1116             :     assert(m_xData->pActUndoArray->pFatherUndoArray);
    1117             : 
    1118             :     // the array/level which we're about to leave
    1119      188751 :     SfxUndoArray* pArrayToLeave = m_xData->pActUndoArray;
    1120             :     // one step up
    1121      188751 :     m_xData->pActUndoArray = m_xData->pActUndoArray->pFatherUndoArray;
    1122             : 
    1123             :     // If no undo actions were added to the list, delete the list action
    1124      188751 :     const size_t nListActionElements = pArrayToLeave->nCurUndoAction;
    1125      188751 :     if ( nListActionElements == 0 )
    1126             :     {
    1127      118342 :         SfxUndoAction* pCurrentAction= m_xData->pActUndoArray->aUndoActions[ m_xData->pActUndoArray->nCurUndoAction-1 ].pAction;
    1128      118342 :         m_xData->pActUndoArray->aUndoActions.Remove( --m_xData->pActUndoArray->nCurUndoAction );
    1129      118342 :         i_guard.markForDeletion( pCurrentAction );
    1130             : 
    1131      118342 :         i_guard.scheduleNotification( &SfxUndoListener::listActionCancelled );
    1132      118342 :         return 0;
    1133             :     }
    1134             : 
    1135             :     // now that it is finally clear the list action is non-trivial, and does participate in the Undo stack, clear
    1136             :     // the redo stack
    1137       70409 :     ImplClearRedo( i_guard, IUndoManager::CurrentLevel );
    1138             : 
    1139       70409 :     SfxUndoAction* pCurrentAction= m_xData->pActUndoArray->aUndoActions[ m_xData->pActUndoArray->nCurUndoAction-1 ].pAction;
    1140       70409 :     SfxListUndoAction* pListAction = dynamic_cast< SfxListUndoAction * >( pCurrentAction );
    1141       70409 :     ENSURE_OR_RETURN( pListAction, "SfxUndoManager::ImplLeaveListAction: list action expected at this position!", nListActionElements );
    1142             : 
    1143       70409 :     if ( i_merge )
    1144             :     {
    1145             :         // merge the list action with its predecessor on the same level
    1146             :         SAL_WARN_IF( m_xData->pActUndoArray->nCurUndoAction <= 1, "svl",
    1147             :             "SfxUndoManager::ImplLeaveListAction: cannot merge the list action if there's no other action on the same level - check this beforehand!" );
    1148           8 :         if ( m_xData->pActUndoArray->nCurUndoAction > 1 )
    1149             :         {
    1150           8 :             SfxUndoAction* pPreviousAction = m_xData->pActUndoArray->aUndoActions[ m_xData->pActUndoArray->nCurUndoAction - 2 ].pAction;
    1151           8 :             m_xData->pActUndoArray->aUndoActions.Remove( m_xData->pActUndoArray->nCurUndoAction - 2 );
    1152           8 :             --m_xData->pActUndoArray->nCurUndoAction;
    1153           8 :             pListAction->aUndoActions.Insert( pPreviousAction, 0 );
    1154           8 :             ++pListAction->nCurUndoAction;
    1155             : 
    1156           8 :             pListAction->SetComment( pPreviousAction->GetComment() );
    1157             :         }
    1158             :     }
    1159             : 
    1160             :     // if the undo array has no comment, try to get it from its children
    1161       70409 :     if ( pListAction->GetComment().isEmpty() )
    1162             :     {
    1163          41 :         for( size_t n = 0; n < pListAction->aUndoActions.size(); n++ )
    1164             :         {
    1165          41 :             if (!pListAction->aUndoActions[n].pAction->GetComment().isEmpty())
    1166             :             {
    1167          21 :                 pListAction->SetComment( pListAction->aUndoActions[n].pAction->GetComment() );
    1168          21 :                 break;
    1169             :             }
    1170             :         }
    1171             :     }
    1172             : 
    1173             :     // notify listeners
    1174       70409 :     i_guard.scheduleNotification( &SfxUndoListener::listActionLeft, pListAction->GetComment() );
    1175             : 
    1176             :     // outta here
    1177       70409 :     return nListActionElements;
    1178             : }
    1179             : 
    1180       37818 : UndoStackMark SfxUndoManager::MarkTopUndoAction()
    1181             : {
    1182       37818 :     UndoManagerGuard aGuard( *m_xData );
    1183             : 
    1184             :     SAL_WARN_IF( IsInListAction(), "svl",
    1185             :             "SfxUndoManager::MarkTopUndoAction(): suspicious call!" );
    1186             :     assert((m_xData->mnMarks + 1) < (m_xData->mnEmptyMark - 1) &&
    1187             :             "SfxUndoManager::MarkTopUndoAction(): mark overflow!");
    1188             : 
    1189       37818 :     size_t const nActionPos = m_xData->pUndoArray->nCurUndoAction;
    1190       37818 :     if (0 == nActionPos)
    1191             :     {
    1192       37118 :         --m_xData->mnEmptyMark;
    1193       37118 :         return m_xData->mnEmptyMark;
    1194             :     }
    1195             : 
    1196         700 :     m_xData->pUndoArray->aUndoActions[ nActionPos-1 ].aMarks.push_back(
    1197        1400 :             ++m_xData->mnMarks );
    1198         700 :     return m_xData->mnMarks;
    1199             : }
    1200             : 
    1201          41 : void SfxUndoManager::RemoveMark( UndoStackMark const i_mark )
    1202             : {
    1203          41 :     UndoManagerGuard aGuard( *m_xData );
    1204             : 
    1205          41 :     if ((m_xData->mnEmptyMark < i_mark) || (MARK_INVALID == i_mark))
    1206             :     {
    1207           0 :         return; // nothing to remove
    1208             :     }
    1209          41 :     else if (i_mark == m_xData->mnEmptyMark)
    1210             :     {
    1211          41 :         --m_xData->mnEmptyMark; // never returned from MarkTop => invalid
    1212          41 :         return;
    1213             :     }
    1214             : 
    1215           0 :     for ( size_t i=0; i<m_xData->pUndoArray->aUndoActions.size(); ++i )
    1216             :     {
    1217           0 :         MarkedUndoAction& rAction = m_xData->pUndoArray->aUndoActions[i];
    1218           0 :         for (   ::std::vector< UndoStackMark >::iterator markPos = rAction.aMarks.begin();
    1219           0 :                 markPos != rAction.aMarks.end();
    1220             :                 ++markPos
    1221             :             )
    1222             :         {
    1223           0 :             if ( *markPos == i_mark )
    1224             :             {
    1225           0 :                 rAction.aMarks.erase( markPos );
    1226           0 :                 return;
    1227             :             }
    1228             :         }
    1229             :     }
    1230           0 :     SAL_WARN("svl", "SfxUndoManager::RemoveMark: mark not found!");
    1231             :         // TODO: this might be too offensive. There are situations where we implicitly remove marks
    1232             :         // without our clients, in particular the client which created the mark, having a chance to know
    1233             :         // about this.
    1234             : }
    1235             : 
    1236          33 : bool SfxUndoManager::HasTopUndoActionMark( UndoStackMark const i_mark )
    1237             : {
    1238          33 :     UndoManagerGuard aGuard( *m_xData );
    1239             : 
    1240          33 :     size_t nActionPos = m_xData->pUndoArray->nCurUndoAction;
    1241          33 :     if ( nActionPos == 0 )
    1242             :     {
    1243           9 :         return (i_mark == m_xData->mnEmptyMark);
    1244             :     }
    1245             : 
    1246             :     const MarkedUndoAction& rAction =
    1247          24 :             m_xData->pUndoArray->aUndoActions[ nActionPos-1 ];
    1248          72 :     for (   ::std::vector< UndoStackMark >::const_iterator markPos = rAction.aMarks.begin();
    1249          48 :             markPos != rAction.aMarks.end();
    1250             :             ++markPos
    1251             :         )
    1252             :     {
    1253           0 :         if ( *markPos == i_mark )
    1254           0 :             return true;
    1255             :     }
    1256             : 
    1257          24 :     return false;
    1258             : }
    1259             : 
    1260             : 
    1261           0 : void SfxUndoManager::RemoveOldestUndoActions( size_t const i_count )
    1262             : {
    1263           0 :     UndoManagerGuard aGuard( *m_xData );
    1264             : 
    1265           0 :     size_t nActionsToRemove = i_count;
    1266           0 :     while ( nActionsToRemove )
    1267             :     {
    1268           0 :         SfxUndoAction* pActionToRemove = m_xData->pUndoArray->aUndoActions[0].pAction;
    1269             : 
    1270           0 :         if ( IsInListAction() && ( m_xData->pUndoArray->nCurUndoAction == 1 ) )
    1271             :         {
    1272             :             assert(!"SfxUndoManager::RemoveOldestUndoActions: cannot remove a not-yet-closed list action!");
    1273           0 :             return;
    1274             :         }
    1275             : 
    1276           0 :         aGuard.markForDeletion( pActionToRemove );
    1277           0 :         m_xData->pUndoArray->aUndoActions.Remove( 0 );
    1278           0 :         --m_xData->pUndoArray->nCurUndoAction;
    1279           0 :         --nActionsToRemove;
    1280           0 :     }
    1281             : }
    1282             : 
    1283           0 : void SfxUndoManager::dumpAsXml(xmlTextWriterPtr pWriter) const
    1284             : {
    1285           0 :     xmlTextWriterStartElement(pWriter, BAD_CAST("sfxUndoManager"));
    1286           0 :     xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nUndoActionCount"), BAD_CAST(OString::number(GetUndoActionCount()).getStr()));
    1287             : 
    1288           0 :     for (size_t i = 0; i < GetUndoActionCount(); ++i)
    1289           0 :         GetUndoAction(i)->dumpAsXml(pWriter);
    1290             : 
    1291           0 :     xmlTextWriterEndElement(pWriter);
    1292           0 : }
    1293             : 
    1294      188465 : struct SfxListUndoAction::Impl
    1295             : {
    1296             :     sal_uInt16 mnId;
    1297             : 
    1298             :     OUString maComment;
    1299             :     OUString maRepeatComment;
    1300             : 
    1301      188752 :     Impl( sal_uInt16 nId, const OUString& rComment, const OUString& rRepeatComment ) :
    1302      188752 :         mnId(nId), maComment(rComment), maRepeatComment(rRepeatComment) {}
    1303             : };
    1304             : 
    1305        3920 : sal_uInt16 SfxListUndoAction::GetId() const
    1306             : {
    1307        3920 :     return mpImpl->mnId;
    1308             : }
    1309             : 
    1310      141279 : OUString SfxListUndoAction::GetComment() const
    1311             : {
    1312      141279 :     return mpImpl->maComment;
    1313             : }
    1314             : 
    1315        5657 : void SfxListUndoAction::SetComment(const OUString& rComment)
    1316             : {
    1317        5657 :     mpImpl->maComment = rComment;
    1318        5657 : }
    1319             : 
    1320          20 : OUString SfxListUndoAction::GetRepeatComment(SfxRepeatTarget &) const
    1321             : {
    1322          20 :     return mpImpl->maRepeatComment;
    1323             : }
    1324             : 
    1325      188752 : SfxListUndoAction::SfxListUndoAction(
    1326             :     const OUString &rComment,
    1327             :     const OUString &rRepeatComment,
    1328             :     sal_uInt16 nId,
    1329             :     SfxUndoArray *pFather ) :
    1330      188752 :     mpImpl(new Impl(nId, rComment, rRepeatComment))
    1331             : {
    1332      188752 :     pFatherUndoArray = pFather;
    1333      188752 :     nMaxUndoActions = USHRT_MAX;
    1334      188752 : }
    1335             : 
    1336      565395 : SfxListUndoAction::~SfxListUndoAction()
    1337             : {
    1338      188465 :     delete mpImpl;
    1339      376930 : }
    1340             : 
    1341          31 : void SfxListUndoAction::Undo()
    1342             : {
    1343         116 :     for(size_t i=nCurUndoAction;i>0;)
    1344          54 :         aUndoActions[--i].pAction->Undo();
    1345          31 :     nCurUndoAction=0;
    1346          31 : }
    1347             : 
    1348             : 
    1349          17 : void SfxListUndoAction::UndoWithContext( SfxUndoContext& i_context )
    1350             : {
    1351          59 :     for(size_t i=nCurUndoAction;i>0;)
    1352          25 :         aUndoActions[--i].pAction->UndoWithContext( i_context );
    1353          17 :     nCurUndoAction=0;
    1354          17 : }
    1355             : 
    1356             : 
    1357           2 : void SfxListUndoAction::Redo()
    1358             : {
    1359           4 :     for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
    1360           2 :         aUndoActions[i].pAction->Redo();
    1361           2 :     nCurUndoAction = aUndoActions.size();
    1362           2 : }
    1363             : 
    1364             : 
    1365           2 : void SfxListUndoAction::RedoWithContext( SfxUndoContext& i_context )
    1366             : {
    1367           4 :     for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
    1368           2 :         aUndoActions[i].pAction->RedoWithContext( i_context );
    1369           2 :     nCurUndoAction = aUndoActions.size();
    1370           2 : }
    1371             : 
    1372             : 
    1373           0 : void SfxListUndoAction::Repeat(SfxRepeatTarget&rTarget)
    1374             : {
    1375           0 :     for(size_t i=0;i<nCurUndoAction;i++)
    1376           0 :         aUndoActions[i].pAction->Repeat(rTarget);
    1377           0 : }
    1378             : 
    1379             : 
    1380          22 : bool SfxListUndoAction::CanRepeat(SfxRepeatTarget&r)  const
    1381             : {
    1382          46 :     for(size_t i=0;i<nCurUndoAction;i++)
    1383             :     {
    1384          24 :         if(!aUndoActions[i].pAction->CanRepeat(r))
    1385           0 :             return false;
    1386             :     }
    1387          22 :     return true;
    1388             : }
    1389             : 
    1390             : 
    1391           0 : bool SfxListUndoAction::Merge( SfxUndoAction *pNextAction )
    1392             : {
    1393           0 :     return !aUndoActions.empty() && aUndoActions[aUndoActions.size()-1].pAction->Merge( pNextAction );
    1394             : }
    1395             : 
    1396           0 : void SfxListUndoAction::dumpAsXml(xmlTextWriterPtr pWriter) const
    1397             : {
    1398           0 :     xmlTextWriterStartElement(pWriter, BAD_CAST("sfxListUndoAction"));
    1399           0 :     xmlTextWriterWriteAttribute(pWriter, BAD_CAST("size"), BAD_CAST(OString::number(aUndoActions.size()).getStr()));
    1400           0 :     SfxUndoAction::dumpAsXml(pWriter);
    1401             : 
    1402           0 :     for (size_t i = 0; i < aUndoActions.size(); ++i)
    1403           0 :         aUndoActions.GetUndoAction(i)->dumpAsXml(pWriter);
    1404             : 
    1405           0 :     xmlTextWriterEndElement(pWriter);
    1406           0 : }
    1407             : 
    1408             : /**
    1409             :  * Creates a LinkAction which points to another UndoManager.
    1410             :  * Gets that UndoManagers current Action and sets it as that UndoManager's
    1411             :  * associated Action.
    1412             :  */
    1413           0 : SfxLinkUndoAction::SfxLinkUndoAction(::svl::IUndoManager *pManager)
    1414             : {
    1415           0 :     pUndoManager = pManager;
    1416           0 :     SfxUndoManager* pUndoManagerImplementation = dynamic_cast< SfxUndoManager* >( pManager );
    1417           0 :     ENSURE_OR_THROW( pUndoManagerImplementation != NULL, "unsupported undo manager implementation!" );
    1418             : 
    1419             :     // yes, this cast is dirty. But reaching into the SfxUndoManager's implementation,
    1420             :     // directly accessing its internal stack, and tampering with an action on that stack
    1421             :     // is dirty, too.
    1422           0 :     if ( pManager->GetMaxUndoActionCount() )
    1423             :     {
    1424           0 :         size_t nPos = pManager->GetUndoActionCount()-1;
    1425           0 :         pAction = pUndoManagerImplementation->m_xData->pActUndoArray->aUndoActions[nPos].pAction;
    1426           0 :         pAction->SetLinkToSfxLinkUndoAction(this);
    1427             :     }
    1428             :     else
    1429           0 :         pAction = 0;
    1430           0 : }
    1431             : 
    1432             : 
    1433           0 : void SfxLinkUndoAction::Undo()
    1434             : {
    1435           0 :     if ( pAction )
    1436           0 :         pUndoManager->Undo();
    1437           0 : }
    1438             : 
    1439             : 
    1440           0 : void SfxLinkUndoAction::Redo()
    1441             : {
    1442           0 :     if ( pAction )
    1443           0 :         pUndoManager->Redo();
    1444           0 : }
    1445             : 
    1446             : 
    1447             : 
    1448           0 : bool SfxLinkUndoAction::CanRepeat(SfxRepeatTarget& r) const
    1449             : {
    1450           0 :     return pAction && pAction->CanRepeat(r);
    1451             : }
    1452             : 
    1453             : 
    1454             : 
    1455             : 
    1456           0 : void SfxLinkUndoAction::Repeat(SfxRepeatTarget&r)
    1457             : {
    1458           0 :     if ( pAction && pAction->CanRepeat( r ) )
    1459           0 :         pAction->Repeat( r );
    1460           0 : }
    1461             : 
    1462             : 
    1463             : 
    1464           0 : OUString SfxLinkUndoAction::GetComment() const
    1465             : {
    1466           0 :     if ( pAction )
    1467           0 :         return pAction->GetComment();
    1468           0 :     return OUString();
    1469             : }
    1470             : 
    1471             : 
    1472             : 
    1473           0 : OUString SfxLinkUndoAction::GetRepeatComment(SfxRepeatTarget&r) const
    1474             : {
    1475           0 :     if ( pAction )
    1476           0 :         return pAction->GetRepeatComment(r);
    1477           0 :     return OUString();
    1478             : }
    1479             : 
    1480             : 
    1481           0 : SfxLinkUndoAction::~SfxLinkUndoAction()
    1482             : {
    1483           0 :     if( pAction )
    1484           0 :         pAction->SetLinkToSfxLinkUndoAction(0);
    1485           0 : }
    1486             : 
    1487             : 
    1488           0 : void SfxLinkUndoAction::LinkedSfxUndoActionDestructed(const SfxUndoAction& rCandidate)
    1489             : {
    1490             :     assert(0 != pAction);
    1491             :     assert(pAction == &rCandidate && "Oops, the destroyed and linked UndoActions differ (!)");
    1492             :     (void)rCandidate;
    1493           0 :     pAction = 0;
    1494           0 : }
    1495             : 
    1496             : 
    1497      483199 : SfxUndoArray::~SfxUndoArray()
    1498             : {
    1499      547719 :     while ( !aUndoActions.empty() )
    1500             :     {
    1501       99943 :         SfxUndoAction *pAction = aUndoActions[ aUndoActions.size() - 1 ].pAction;
    1502       99943 :         aUndoActions.Remove( aUndoActions.size() - 1 );
    1503       99943 :         delete pAction;
    1504             :     }
    1505      259311 : }
    1506             : 
    1507             : 
    1508           0 : sal_uInt16 SfxLinkUndoAction::GetId() const
    1509             : {
    1510           0 :       return pAction ? pAction->GetId() : 0;
    1511         819 : }
    1512             : 
    1513             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11