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