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