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