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