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 640 if(m_pData->pActUndoArray->nCurUndoAction) 641 { 642 --m_pData->pActUndoArray->nCurUndoAction; 643 } 644 else 645 { 646 OSL_ENSURE(false, "CurrentUndoAction going negative (!)"); 647 } 648 } 649 } 650 651 // append new action 652 m_pData->pActUndoArray->aUndoActions.Insert( pAction, m_pData->pActUndoArray->nCurUndoAction++ ); 653 return true; 654 } 655 656 //------------------------------------------------------------------------ 657 658 void SfxUndoManager::AddUndoAction( SfxUndoAction *pAction, sal_Bool bTryMerge ) 659 { 660 UndoManagerGuard aGuard( *m_pData ); 661 662 // add 663 if ( ImplAddUndoAction_NoNotify( pAction, bTryMerge, true, aGuard ) ) 664 { 665 // notify listeners 666 aGuard.scheduleNotification( &SfxUndoListener::undoActionAdded, pAction->GetComment() ); 667 } 668 } 669 670 //------------------------------------------------------------------------ 671 672 size_t SfxUndoManager::GetUndoActionCount( bool const i_currentLevel ) const 673 { 674 UndoManagerGuard aGuard( *m_pData ); 675 const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray; 676 return pUndoArray->nCurUndoAction; 677 } 678 679 //------------------------------------------------------------------------ 680 681 XubString SfxUndoManager::GetUndoActionComment( size_t nNo, bool const i_currentLevel ) const 682 { 683 UndoManagerGuard aGuard( *m_pData ); 684 685 String sComment; 686 const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray; 687 DBG_ASSERT( nNo < pUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoActionComment: illegal index!" ); 688 if( nNo < pUndoArray->nCurUndoAction ) 689 { 690 sComment = pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction - 1 - nNo ].pAction->GetComment(); 691 } 692 return sComment; 693 } 694 695 //------------------------------------------------------------------------ 696 697 sal_uInt16 SfxUndoManager::GetUndoActionId() const 698 { 699 UndoManagerGuard aGuard( *m_pData ); 700 701 DBG_ASSERT( m_pData->pActUndoArray->nCurUndoAction > 0, "svl::SfxUndoManager::GetUndoActionId(), illegal id!" ); 702 if ( m_pData->pActUndoArray->nCurUndoAction == 0 ) 703 return 0; 704 return m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1].pAction->GetId(); 705 } 706 707 //------------------------------------------------------------------------ 708 709 SfxUndoAction* SfxUndoManager::GetUndoAction( size_t nNo ) const 710 { 711 UndoManagerGuard aGuard( *m_pData ); 712 713 DBG_ASSERT( nNo < m_pData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoAction(), illegal id!" ); 714 if( nNo >= m_pData->pActUndoArray->nCurUndoAction ) 715 return NULL; 716 return m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1-nNo].pAction; 717 } 718 719 //------------------------------------------------------------------------ 720 721 /** clears the redo stack and removes the top undo action */ 722 void SfxUndoManager::RemoveLastUndoAction() 723 { 724 UndoManagerGuard aGuard( *m_pData ); 725 726 ENSURE_OR_RETURN_VOID( m_pData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::RemoveLastUndoAction(), no action to remove?!" ); 727 728 m_pData->pActUndoArray->nCurUndoAction--; 729 730 // delete redo-actions and top action 731 for ( size_t nPos = m_pData->pActUndoArray->aUndoActions.size(); nPos > m_pData->pActUndoArray->nCurUndoAction; --nPos ) 732 { 733 aGuard.markForDeletion( m_pData->pActUndoArray->aUndoActions[nPos-1].pAction ); 734 } 735 736 m_pData->pActUndoArray->aUndoActions.Remove( 737 m_pData->pActUndoArray->nCurUndoAction, 738 m_pData->pActUndoArray->aUndoActions.size() - m_pData->pActUndoArray->nCurUndoAction ); 739 } 740 741 //------------------------------------------------------------------------ 742 743 bool SfxUndoManager::IsDoing() const 744 { 745 UndoManagerGuard aGuard( *m_pData ); 746 return m_pData->mbDoing; 747 } 748 749 //------------------------------------------------------------------------ 750 751 sal_Bool SfxUndoManager::Undo() 752 { 753 return ImplUndo( NULL ); 754 } 755 756 //------------------------------------------------------------------------ 757 758 sal_Bool SfxUndoManager::UndoWithContext( SfxUndoContext& i_context ) 759 { 760 return ImplUndo( &i_context ); 761 } 762 763 //------------------------------------------------------------------------ 764 765 sal_Bool SfxUndoManager::ImplUndo( SfxUndoContext* i_contextOrNull ) 766 { 767 UndoManagerGuard aGuard( *m_pData ); 768 OSL_ENSURE( !IsDoing(), "SfxUndoManager::Undo: *nested* Undo/Redo actions? How this?" ); 769 770 ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing ); 771 LockGuard aLockGuard( *this ); 772 773 if ( ImplIsInListAction_Lock() ) 774 { 775 OSL_ENSURE( false, "SfxUndoManager::Undo: not possible when within a list action!" ); 776 return sal_False; 777 } 778 779 if ( m_pData->pActUndoArray->nCurUndoAction == 0 ) 780 { 781 OSL_ENSURE( false, "SfxUndoManager::Undo: undo stack is empty!" ); 782 return sal_False; 783 } 784 785 SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ --m_pData->pActUndoArray->nCurUndoAction ].pAction; 786 const String sActionComment = pAction->GetComment(); 787 try 788 { 789 // clear the guard/mutex before calling into the SfxUndoAction - this can be an extension-implemented UNO component 790 // nowadays ... 791 aGuard.clear(); 792 if ( i_contextOrNull != NULL ) 793 pAction->UndoWithContext( *i_contextOrNull ); 794 else 795 pAction->Undo(); 796 aGuard.reset(); 797 } 798 catch( ... ) 799 { 800 aGuard.reset(); 801 802 // in theory, somebody might have tampered with all of *m_pData while the mutex was unlocked. So, see if 803 // we still find pAction in our current Undo array 804 size_t nCurAction = 0; 805 while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() ) 806 { 807 if ( m_pData->pActUndoArray->aUndoActions[ nCurAction++ ].pAction == pAction ) 808 { 809 // the Undo action is still there ... 810 // assume the error is a permanent failure, and clear the Undo stack 811 ImplClearUndo( aGuard ); 812 throw; 813 } 814 } 815 OSL_ENSURE( false, "SfxUndoManager::Undo: can't clear the Undo stack after the failure - some other party was faster ..." ); 816 throw; 817 } 818 819 aGuard.scheduleNotification( &SfxUndoListener::actionUndone, sActionComment ); 820 821 return sal_True; 822 } 823 824 //------------------------------------------------------------------------ 825 826 size_t SfxUndoManager::GetRedoActionCount( bool const i_currentLevel ) const 827 { 828 UndoManagerGuard aGuard( *m_pData ); 829 return ImplGetRedoActionCount_Lock( i_currentLevel ); 830 } 831 832 //------------------------------------------------------------------------ 833 834 size_t SfxUndoManager::ImplGetRedoActionCount_Lock( bool const i_currentLevel ) const 835 { 836 const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray; 837 return pUndoArray->aUndoActions.size() - pUndoArray->nCurUndoAction; 838 } 839 840 //------------------------------------------------------------------------ 841 842 SfxUndoAction* SfxUndoManager::GetRedoAction( size_t nNo, bool const i_currentLevel ) const 843 { 844 UndoManagerGuard aGuard( *m_pData ); 845 846 const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray; 847 if ( (pUndoArray->nCurUndoAction + nNo) > pUndoArray->aUndoActions.size() ) 848 { 849 return NULL; 850 } 851 return pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction; 852 } 853 854 //------------------------------------------------------------------------ 855 856 XubString SfxUndoManager::GetRedoActionComment( size_t nNo, bool const i_currentLevel ) const 857 { 858 String sComment; 859 UndoManagerGuard aGuard( *m_pData ); 860 const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray; 861 if ( (pUndoArray->nCurUndoAction + nNo) < pUndoArray->aUndoActions.size() ) 862 { 863 sComment = pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction->GetComment(); 864 } 865 return sComment; 866 } 867 868 //------------------------------------------------------------------------ 869 870 sal_Bool SfxUndoManager::Redo() 871 { 872 return ImplRedo( NULL ); 873 } 874 875 //------------------------------------------------------------------------ 876 877 sal_Bool SfxUndoManager::RedoWithContext( SfxUndoContext& i_context ) 878 { 879 return ImplRedo( &i_context ); 880 } 881 882 //------------------------------------------------------------------------ 883 884 sal_Bool SfxUndoManager::ImplRedo( SfxUndoContext* i_contextOrNull ) 885 { 886 UndoManagerGuard aGuard( *m_pData ); 887 OSL_ENSURE( !IsDoing(), "SfxUndoManager::Redo: *nested* Undo/Redo actions? How this?" ); 888 889 ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing ); 890 LockGuard aLockGuard( *this ); 891 892 if ( ImplIsInListAction_Lock() ) 893 { 894 OSL_ENSURE( false, "SfxUndoManager::Redo: not possible when within a list action!" ); 895 return sal_False; 896 } 897 898 if ( m_pData->pActUndoArray->nCurUndoAction >= m_pData->pActUndoArray->aUndoActions.size() ) 899 { 900 OSL_ENSURE( false, "SfxUndoManager::Redo: redo stack is empty!" ); 901 return sal_False; 902 } 903 904 SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction++ ].pAction; 905 const String sActionComment = pAction->GetComment(); 906 try 907 { 908 // clear the guard/mutex before calling into the SfxUndoAction - this can be a extension-implemented UNO component 909 // nowadays ... 910 aGuard.clear(); 911 if ( i_contextOrNull != NULL ) 912 pAction->RedoWithContext( *i_contextOrNull ); 913 else 914 pAction->Redo(); 915 aGuard.reset(); 916 } 917 catch( ... ) 918 { 919 aGuard.reset(); 920 921 // in theory, somebody might have tampered with all of *m_pData while the mutex was unlocked. So, see if 922 // we still find pAction in our current Undo array 923 size_t nCurAction = 0; 924 while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() ) 925 { 926 if ( m_pData->pActUndoArray->aUndoActions[ nCurAction ].pAction == pAction ) 927 { 928 // the Undo action is still there ... 929 // assume the error is a permanent failure, and clear the Undo stack 930 ImplClearRedo( aGuard, IUndoManager::CurrentLevel ); 931 throw; 932 } 933 ++nCurAction; 934 } 935 OSL_ENSURE( false, "SfxUndoManager::Redo: can't clear the Undo stack after the failure - some other party was faster ..." ); 936 throw; 937 } 938 939 aGuard.scheduleNotification( &SfxUndoListener::actionRedone, sActionComment ); 940 941 return sal_True; 942 } 943 944 //------------------------------------------------------------------------ 945 946 size_t SfxUndoManager::GetRepeatActionCount() const 947 { 948 UndoManagerGuard aGuard( *m_pData ); 949 return m_pData->pActUndoArray->aUndoActions.size(); 950 } 951 952 //------------------------------------------------------------------------ 953 954 XubString SfxUndoManager::GetRepeatActionComment( SfxRepeatTarget &rTarget) const 955 { 956 UndoManagerGuard aGuard( *m_pData ); 957 return m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.size() - 1 ].pAction 958 ->GetRepeatComment(rTarget); 959 } 960 961 //------------------------------------------------------------------------ 962 963 sal_Bool SfxUndoManager::Repeat( SfxRepeatTarget &rTarget ) 964 { 965 UndoManagerGuard aGuard( *m_pData ); 966 if ( !m_pData->pActUndoArray->aUndoActions.empty() ) 967 { 968 SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.size() - 1 ].pAction; 969 aGuard.clear(); 970 if ( pAction->CanRepeat( rTarget ) ) 971 pAction->Repeat( rTarget ); 972 return sal_True; 973 } 974 975 return sal_False; 976 } 977 978 //------------------------------------------------------------------------ 979 980 sal_Bool SfxUndoManager::CanRepeat( SfxRepeatTarget &rTarget ) const 981 { 982 UndoManagerGuard aGuard( *m_pData ); 983 if ( !m_pData->pActUndoArray->aUndoActions.empty() ) 984 { 985 size_t nActionNo = m_pData->pActUndoArray->aUndoActions.size() - 1; 986 return m_pData->pActUndoArray->aUndoActions[nActionNo].pAction->CanRepeat(rTarget); 987 } 988 return sal_False; 989 } 990 991 //------------------------------------------------------------------------ 992 993 void SfxUndoManager::AddUndoListener( SfxUndoListener& i_listener ) 994 { 995 UndoManagerGuard aGuard( *m_pData ); 996 m_pData->aListeners.push_back( &i_listener ); 997 } 998 999 //------------------------------------------------------------------------ 1000 1001 void SfxUndoManager::RemoveUndoListener( SfxUndoListener& i_listener ) 1002 { 1003 UndoManagerGuard aGuard( *m_pData ); 1004 for ( UndoListeners::iterator lookup = m_pData->aListeners.begin(); 1005 lookup != m_pData->aListeners.end(); 1006 ++lookup 1007 ) 1008 { 1009 if ( (*lookup) == &i_listener ) 1010 { 1011 m_pData->aListeners.erase( lookup ); 1012 break; 1013 } 1014 } 1015 } 1016 1017 //------------------------------------------------------------------------ 1018 1019 void SfxUndoManager::EnterListAction( 1020 const XubString& rComment, const XubString &rRepeatComment, sal_uInt16 nId ) 1021 1022 /* [Beschreibung] 1023 1024 Fuegt eine ListUndoAction ein und setzt dessen UndoArray als aktuelles. 1025 */ 1026 1027 { 1028 UndoManagerGuard aGuard( *m_pData ); 1029 1030 if( !ImplIsUndoEnabled_Lock() ) 1031 return; 1032 1033 if ( !m_pData->pUndoArray->nMaxUndoActions ) 1034 return; 1035 1036 m_pData->pFatherUndoArray = m_pData->pActUndoArray; 1037 SfxListUndoAction* pAction = new SfxListUndoAction( rComment, rRepeatComment, nId, m_pData->pActUndoArray ); 1038 OSL_VERIFY( ImplAddUndoAction_NoNotify( pAction, false, false, aGuard ) ); 1039 // expected to succeed: all conditions under which it could fail should have been checked already 1040 m_pData->pActUndoArray = pAction; 1041 1042 // notification 1043 aGuard.scheduleNotification( &SfxUndoListener::listActionEntered, rComment ); 1044 } 1045 1046 //------------------------------------------------------------------------ 1047 1048 bool SfxUndoManager::IsInListAction() const 1049 { 1050 UndoManagerGuard aGuard( *m_pData ); 1051 return ImplIsInListAction_Lock(); 1052 } 1053 1054 //------------------------------------------------------------------------ 1055 1056 bool SfxUndoManager::ImplIsInListAction_Lock() const 1057 { 1058 return ( m_pData->pActUndoArray != m_pData->pUndoArray ); 1059 } 1060 1061 //------------------------------------------------------------------------ 1062 1063 size_t SfxUndoManager::GetListActionDepth() const 1064 { 1065 UndoManagerGuard aGuard( *m_pData ); 1066 size_t nDepth(0); 1067 1068 SfxUndoArray* pLookup( m_pData->pActUndoArray ); 1069 while ( pLookup != m_pData->pUndoArray ) 1070 { 1071 pLookup = pLookup->pFatherUndoArray; 1072 ++nDepth; 1073 } 1074 1075 return nDepth; 1076 } 1077 1078 //------------------------------------------------------------------------ 1079 1080 size_t SfxUndoManager::LeaveListAction() 1081 { 1082 UndoManagerGuard aGuard( *m_pData ); 1083 size_t nCount = ImplLeaveListAction( false, aGuard ); 1084 1085 if ( m_pData->mbClearUntilTopLevel ) 1086 { 1087 ImplClearCurrentLevel_NoNotify( aGuard ); 1088 if ( !ImplIsInListAction_Lock() ) 1089 { 1090 m_pData->mbClearUntilTopLevel = false; 1091 aGuard.scheduleNotification( &SfxUndoListener::cleared ); 1092 } 1093 nCount = 0; 1094 } 1095 1096 return nCount; 1097 } 1098 1099 //------------------------------------------------------------------------ 1100 1101 size_t SfxUndoManager::LeaveAndMergeListAction() 1102 { 1103 UndoManagerGuard aGuard( *m_pData ); 1104 return ImplLeaveListAction( true, aGuard ); 1105 } 1106 1107 //------------------------------------------------------------------------ 1108 1109 size_t SfxUndoManager::ImplLeaveListAction( const bool i_merge, UndoManagerGuard& i_guard ) 1110 { 1111 if ( !ImplIsUndoEnabled_Lock() ) 1112 return 0; 1113 1114 if ( !m_pData->pUndoArray->nMaxUndoActions ) 1115 return 0; 1116 1117 if( !ImplIsInListAction_Lock() ) 1118 { 1119 DBG_ERROR( "svl::SfxUndoManager::ImplLeaveListAction, called without calling EnterListAction()!" ); 1120 return 0; 1121 } 1122 1123 DBG_ASSERT( m_pData->pActUndoArray->pFatherUndoArray, "SfxUndoManager::ImplLeaveListAction, no father undo array!?" ); 1124 1125 // the array/level which we're about to leave 1126 SfxUndoArray* pArrayToLeave = m_pData->pActUndoArray; 1127 // one step up 1128 m_pData->pActUndoArray = m_pData->pActUndoArray->pFatherUndoArray; 1129 1130 // If no undo actions were added to the list, delete the list action 1131 const size_t nListActionElements = pArrayToLeave->nCurUndoAction; 1132 if ( nListActionElements == 0 ) 1133 { 1134 SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction; 1135 m_pData->pActUndoArray->aUndoActions.Remove( --m_pData->pActUndoArray->nCurUndoAction ); 1136 i_guard.markForDeletion( pCurrentAction ); 1137 1138 i_guard.scheduleNotification( &SfxUndoListener::listActionCancelled ); 1139 return 0; 1140 } 1141 1142 // now that it is finally clear the list action is non-trivial, and does participate in the Undo stack, clear 1143 // the redo stack 1144 ImplClearRedo( i_guard, IUndoManager::CurrentLevel ); 1145 1146 SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction; 1147 SfxListUndoAction* pListAction = dynamic_cast< SfxListUndoAction * >( pCurrentAction ); 1148 ENSURE_OR_RETURN( pListAction, "SfxUndoManager::ImplLeaveListAction: list action expected at this position!", nListActionElements ); 1149 1150 if ( i_merge ) 1151 { 1152 // merge the list action with its predecessor on the same level 1153 OSL_ENSURE( m_pData->pActUndoArray->nCurUndoAction > 1, 1154 "SfxUndoManager::ImplLeaveListAction: cannot merge the list action if there's no other action on the same level - check this beforehand!" ); 1155 if ( m_pData->pActUndoArray->nCurUndoAction > 1 ) 1156 { 1157 SfxUndoAction* pPreviousAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction - 2 ].pAction; 1158 m_pData->pActUndoArray->aUndoActions.Remove( m_pData->pActUndoArray->nCurUndoAction - 2 ); 1159 --m_pData->pActUndoArray->nCurUndoAction; 1160 pListAction->aUndoActions.Insert( pPreviousAction, 0 ); 1161 ++pListAction->nCurUndoAction; 1162 1163 pListAction->SetComment( pPreviousAction->GetComment() ); 1164 } 1165 } 1166 1167 // if the undo array has no comment, try to get it from its children 1168 if ( pListAction->GetComment().Len() == 0 ) 1169 { 1170 for( size_t n = 0; n < pListAction->aUndoActions.size(); n++ ) 1171 { 1172 if( pListAction->aUndoActions[n].pAction->GetComment().Len() ) 1173 { 1174 pListAction->SetComment( pListAction->aUndoActions[n].pAction->GetComment() ); 1175 break; 1176 } 1177 } 1178 } 1179 1180 // notify listeners 1181 i_guard.scheduleNotification( &SfxUndoListener::listActionLeft, pListAction->GetComment() ); 1182 1183 // outta here 1184 return nListActionElements; 1185 } 1186 1187 //------------------------------------------------------------------------ 1188 UndoStackMark SfxUndoManager::MarkTopUndoAction() 1189 { 1190 UndoManagerGuard aGuard( *m_pData ); 1191 1192 OSL_ENSURE( !IsInListAction(), 1193 "SfxUndoManager::MarkTopUndoAction(): suspicious call!" ); 1194 OSL_ENSURE((m_pData->mnMarks + 1) < (m_pData->mnEmptyMark - 1), 1195 "SfxUndoManager::MarkTopUndoAction(): mark overflow!"); 1196 1197 size_t const nActionPos = m_pData->pUndoArray->nCurUndoAction; 1198 if (0 == nActionPos) 1199 { 1200 --m_pData->mnEmptyMark; 1201 return m_pData->mnEmptyMark; 1202 } 1203 1204 m_pData->pUndoArray->aUndoActions[ nActionPos-1 ].aMarks.push_back( 1205 ++m_pData->mnMarks ); 1206 return m_pData->mnMarks; 1207 } 1208 1209 //------------------------------------------------------------------------ 1210 void SfxUndoManager::RemoveMark( UndoStackMark const i_mark ) 1211 { 1212 UndoManagerGuard aGuard( *m_pData ); 1213 1214 if ((m_pData->mnEmptyMark < i_mark) || (MARK_INVALID == i_mark)) 1215 { 1216 return; // nothing to remove 1217 } 1218 else if (i_mark == m_pData->mnEmptyMark) 1219 { 1220 --m_pData->mnEmptyMark; // never returned from MarkTop => invalid 1221 return; 1222 } 1223 1224 for ( size_t i=0; i<m_pData->pUndoArray->aUndoActions.size(); ++i ) 1225 { 1226 MarkedUndoAction& rAction = m_pData->pUndoArray->aUndoActions[i]; 1227 for ( ::std::vector< UndoStackMark >::iterator markPos = rAction.aMarks.begin(); 1228 markPos != rAction.aMarks.end(); 1229 ++markPos 1230 ) 1231 { 1232 if ( *markPos == i_mark ) 1233 { 1234 rAction.aMarks.erase( markPos ); 1235 return; 1236 } 1237 } 1238 } 1239 OSL_ENSURE( false, "SfxUndoManager::RemoveMark: mark not found!" ); 1240 // TODO: this might be too offensive. There are situations where we implicitly remove marks 1241 // without our clients, in particular the client which created the mark, having a chance to know 1242 // about this. 1243 } 1244 1245 //------------------------------------------------------------------------ 1246 bool SfxUndoManager::HasTopUndoActionMark( UndoStackMark const i_mark ) 1247 { 1248 UndoManagerGuard aGuard( *m_pData ); 1249 1250 size_t nActionPos = m_pData->pUndoArray->nCurUndoAction; 1251 if ( nActionPos == 0 ) 1252 { 1253 return (i_mark == m_pData->mnEmptyMark); 1254 } 1255 1256 const MarkedUndoAction& rAction = 1257 m_pData->pUndoArray->aUndoActions[ nActionPos-1 ]; 1258 for ( ::std::vector< UndoStackMark >::const_iterator markPos = rAction.aMarks.begin(); 1259 markPos != rAction.aMarks.end(); 1260 ++markPos 1261 ) 1262 { 1263 if ( *markPos == i_mark ) 1264 return true; 1265 } 1266 1267 return false; 1268 } 1269 1270 //------------------------------------------------------------------------ 1271 1272 void SfxUndoManager::RemoveOldestUndoActions( size_t const i_count ) 1273 { 1274 UndoManagerGuard aGuard( *m_pData ); 1275 1276 size_t nActionsToRemove = i_count; 1277 while ( nActionsToRemove ) 1278 { 1279 SfxUndoAction* pActionToRemove = m_pData->pUndoArray->aUndoActions[0].pAction; 1280 1281 if ( IsInListAction() && ( m_pData->pUndoArray->nCurUndoAction == 1 ) ) 1282 { 1283 OSL_ENSURE( false, "SfxUndoManager::RemoveOldestUndoActions: cannot remove a not-yet-closed list action!" ); 1284 return; 1285 } 1286 1287 aGuard.markForDeletion( pActionToRemove ); 1288 m_pData->pUndoArray->aUndoActions.Remove( 0 ); 1289 --m_pData->pUndoArray->nCurUndoAction; 1290 --nActionsToRemove; 1291 } 1292 } 1293 1294 //------------------------------------------------------------------------ 1295 1296 sal_uInt16 SfxListUndoAction::GetId() const 1297 { 1298 return nId; 1299 } 1300 1301 //------------------------------------------------------------------------ 1302 1303 XubString SfxListUndoAction::GetComment() const 1304 { 1305 return aComment; 1306 } 1307 1308 //------------------------------------------------------------------------ 1309 1310 void SfxListUndoAction::SetComment( const UniString& rComment ) 1311 { 1312 aComment = rComment; 1313 } 1314 1315 //------------------------------------------------------------------------ 1316 1317 XubString SfxListUndoAction::GetRepeatComment(SfxRepeatTarget &) const 1318 { 1319 return aRepeatComment; 1320 } 1321 1322 1323 //------------------------------------------------------------------------ 1324 1325 SfxListUndoAction::SfxListUndoAction 1326 ( 1327 const XubString &rComment, 1328 const XubString rRepeatComment, 1329 sal_uInt16 Id, 1330 SfxUndoArray *pFather 1331 ) 1332 : nId(Id), aComment(rComment), aRepeatComment(rRepeatComment) 1333 { 1334 pFatherUndoArray = pFather; 1335 nMaxUndoActions = USHRT_MAX; 1336 } 1337 1338 //------------------------------------------------------------------------ 1339 1340 void SfxListUndoAction::Undo() 1341 { 1342 for(size_t i=nCurUndoAction;i>0;) 1343 aUndoActions[--i].pAction->Undo(); 1344 nCurUndoAction=0; 1345 } 1346 1347 //------------------------------------------------------------------------ 1348 1349 void SfxListUndoAction::UndoWithContext( SfxUndoContext& i_context ) 1350 { 1351 for(size_t i=nCurUndoAction;i>0;) 1352 aUndoActions[--i].pAction->UndoWithContext( i_context ); 1353 nCurUndoAction=0; 1354 } 1355 1356 //------------------------------------------------------------------------ 1357 1358 void SfxListUndoAction::Redo() 1359 { 1360 for(size_t i=nCurUndoAction;i<aUndoActions.size();i++) 1361 aUndoActions[i].pAction->Redo(); 1362 nCurUndoAction = aUndoActions.size(); 1363 } 1364 1365 //------------------------------------------------------------------------ 1366 1367 void SfxListUndoAction::RedoWithContext( SfxUndoContext& i_context ) 1368 { 1369 for(size_t i=nCurUndoAction;i<aUndoActions.size();i++) 1370 aUndoActions[i].pAction->RedoWithContext( i_context ); 1371 nCurUndoAction = aUndoActions.size(); 1372 } 1373 1374 //------------------------------------------------------------------------ 1375 1376 void SfxListUndoAction::Repeat(SfxRepeatTarget&rTarget) 1377 { 1378 for(size_t i=0;i<nCurUndoAction;i++) 1379 aUndoActions[i].pAction->Repeat(rTarget); 1380 } 1381 1382 //------------------------------------------------------------------------ 1383 1384 sal_Bool SfxListUndoAction::CanRepeat(SfxRepeatTarget&r) const 1385 { 1386 for(size_t i=0;i<nCurUndoAction;i++) 1387 if(!aUndoActions[i].pAction->CanRepeat(r)) 1388 return sal_False; 1389 return sal_True; 1390 } 1391 1392 //------------------------------------------------------------------------ 1393 1394 sal_Bool SfxListUndoAction::Merge( SfxUndoAction *pNextAction ) 1395 { 1396 return !aUndoActions.empty() && aUndoActions[aUndoActions.size()-1].pAction->Merge( pNextAction ); 1397 } 1398 1399 //------------------------------------------------------------------------ 1400 1401 SfxLinkUndoAction::SfxLinkUndoAction(::svl::IUndoManager *pManager) 1402 /* [Beschreibung] 1403 1404 Richtet eine LinkAction ein, die auf einen weiteren UndoManager zeigt. 1405 Holt sich als zugehoerige Action des weiteren UndoManagers dessen 1406 aktuelle Action. 1407 */ 1408 1409 { 1410 pUndoManager = pManager; 1411 SfxUndoManager* pUndoManagerImplementation = dynamic_cast< SfxUndoManager* >( pManager ); 1412 ENSURE_OR_THROW( pUndoManagerImplementation != NULL, "unsupported undo manager implementation!" ); 1413 // yes, this cast is dirty. But reaching into the the SfxUndoManager's implementation, 1414 // directly accessing its internal stack, and tampering with an action on that stack 1415 // is dirty, too. 1416 if ( pManager->GetMaxUndoActionCount() ) 1417 { 1418 size_t nPos = pManager->GetUndoActionCount()-1; 1419 pAction = pUndoManagerImplementation->m_pData->pActUndoArray->aUndoActions[nPos].pAction; 1420 pAction->SetLinkToSfxLinkUndoAction(this); 1421 } 1422 else 1423 pAction = 0; 1424 } 1425 1426 //------------------------------------------------------------------------ 1427 1428 void SfxLinkUndoAction::Undo() 1429 { 1430 if ( pAction ) 1431 pUndoManager->Undo(); 1432 } 1433 1434 //------------------------------------------------------------------------ 1435 1436 void SfxLinkUndoAction::Redo() 1437 { 1438 if ( pAction ) 1439 pUndoManager->Redo(); 1440 } 1441 1442 //------------------------------------------------------------------------ 1443 1444 1445 sal_Bool SfxLinkUndoAction::CanRepeat(SfxRepeatTarget& r) const 1446 { 1447 return pAction && pAction->CanRepeat(r); 1448 } 1449 1450 1451 //------------------------------------------------------------------------ 1452 1453 1454 void SfxLinkUndoAction::Repeat(SfxRepeatTarget&r) 1455 { 1456 if ( pAction && pAction->CanRepeat( r ) ) 1457 pAction->Repeat( r ); 1458 } 1459 1460 1461 //------------------------------------------------------------------------ 1462 1463 XubString SfxLinkUndoAction::GetComment() const 1464 { 1465 if ( pAction ) 1466 return pAction->GetComment(); 1467 else 1468 return XubString(); 1469 } 1470 1471 1472 //------------------------------------------------------------------------ 1473 1474 XubString SfxLinkUndoAction::GetRepeatComment(SfxRepeatTarget&r) const 1475 { 1476 if ( pAction ) 1477 return pAction->GetRepeatComment(r); 1478 else 1479 return XubString(); 1480 } 1481 1482 //------------------------------------------------------------------------ 1483 1484 SfxLinkUndoAction::~SfxLinkUndoAction() 1485 { 1486 if( pAction ) 1487 pAction->SetLinkToSfxLinkUndoAction(0); 1488 } 1489 1490 //------------------------------------------------------------------------ 1491 1492 void SfxLinkUndoAction::LinkedSfxUndoActionDestructed(const SfxUndoAction& rCandidate) 1493 { 1494 OSL_ENSURE(0 != pAction, "OOps, we have no linked SfxUndoAction (!)"); 1495 OSL_ENSURE(pAction == &rCandidate, "OOps, the destroyed and linked UndoActions differ (!)"); 1496 (void)rCandidate; 1497 pAction = 0; 1498 } 1499 1500 //------------------------------------------------------------------------ 1501 1502 SfxUndoArray::~SfxUndoArray() 1503 { 1504 while ( !aUndoActions.empty() ) 1505 { 1506 SfxUndoAction *pAction = aUndoActions[ aUndoActions.size() - 1 ].pAction; 1507 aUndoActions.Remove( aUndoActions.size() - 1 ); 1508 delete pAction; 1509 } 1510 } 1511 1512 1513 sal_uInt16 SfxLinkUndoAction::GetId() const 1514 { 1515 return pAction ? pAction->GetId() : 0; 1516 } 1517