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_sw.hxx" 26 27 #include <UndoManager.hxx> 28 29 #include <unotools/undoopt.hxx> 30 31 #include <vcl/wrkwin.hxx> 32 33 #include <svx/svdmodel.hxx> 34 35 #include <swmodule.hxx> 36 #include <doc.hxx> 37 #include <ndarr.hxx> 38 #include <pam.hxx> 39 #include <ndtxt.hxx> 40 #include <swundo.hxx> 41 #include <UndoCore.hxx> 42 #include <rolbck.hxx> 43 #include <undo.hrc> 44 #include <editsh.hxx> 45 #include <unobaseclass.hxx> 46 #include <limits> 47 48 #include <limits> 49 50 using namespace ::com::sun::star; 51 52 53 // the undo array should never grow beyond this limit: 54 #define UNDO_ACTION_LIMIT (USHRT_MAX - 1000) 55 56 57 // UndoManager /////////////////////////////////////////////////////////// 58 59 namespace sw { 60 61 UndoManager::UndoManager(::std::auto_ptr<SwNodes> pUndoNodes, 62 IDocumentDrawModelAccess & rDrawModelAccess, 63 IDocumentRedlineAccess & rRedlineAccess, 64 IDocumentState & rState) 65 : m_rDrawModelAccess(rDrawModelAccess) 66 , m_rRedlineAccess(rRedlineAccess) 67 , m_rState(rState) 68 , m_pUndoNodes(pUndoNodes) 69 , m_bGroupUndo(true) 70 , m_bDrawUndo(true) 71 , m_bLockUndoNoModifiedPosition(false) 72 , m_UndoSaveMark(MARK_INVALID) 73 { 74 OSL_ASSERT(m_pUndoNodes.get()); 75 // writer expects it to be disabled initially 76 // Undo is enabled by SwEditShell constructor 77 SdrUndoManager::EnableUndo(false); 78 } 79 80 SwNodes const& UndoManager::GetUndoNodes() const 81 { 82 return *m_pUndoNodes; 83 } 84 85 SwNodes & UndoManager::GetUndoNodes() 86 { 87 return *m_pUndoNodes; 88 } 89 90 bool UndoManager::IsUndoNodes(SwNodes const& rNodes) const 91 { 92 return & rNodes == m_pUndoNodes.get(); 93 } 94 95 void UndoManager::DoUndo(bool const bDoUndo) 96 { 97 if(!isTextEditActive()) 98 { 99 EnableUndo(bDoUndo); 100 101 SdrModel *const pSdrModel = m_rDrawModelAccess.GetDrawModel(); 102 if( pSdrModel ) 103 { 104 pSdrModel->EnableUndo(bDoUndo); 105 } 106 } 107 } 108 109 bool UndoManager::DoesUndo() const 110 { 111 if(isTextEditActive()) 112 { 113 return false; 114 } 115 else 116 { 117 return IsUndoEnabled(); 118 } 119 } 120 121 void UndoManager::DoGroupUndo(bool const bDoUndo) 122 { 123 m_bGroupUndo = bDoUndo; 124 } 125 126 bool UndoManager::DoesGroupUndo() const 127 { 128 return m_bGroupUndo; 129 } 130 131 void UndoManager::DoDrawUndo(bool const bDoUndo) 132 { 133 m_bDrawUndo = bDoUndo; 134 } 135 136 bool UndoManager::DoesDrawUndo() const 137 { 138 return m_bDrawUndo; 139 } 140 141 142 bool UndoManager::IsUndoNoResetModified() const 143 { 144 return MARK_INVALID == m_UndoSaveMark; 145 } 146 147 void UndoManager::SetUndoNoResetModified() 148 { 149 if (MARK_INVALID != m_UndoSaveMark) 150 { 151 RemoveMark(m_UndoSaveMark); 152 m_UndoSaveMark = MARK_INVALID; 153 } 154 } 155 156 void UndoManager::SetUndoNoModifiedPosition() 157 { 158 if (!m_bLockUndoNoModifiedPosition) 159 { 160 m_UndoSaveMark = MarkTopUndoAction(); 161 } 162 } 163 164 void UndoManager::LockUndoNoModifiedPosition() 165 { 166 m_bLockUndoNoModifiedPosition = true; 167 } 168 169 void UndoManager::UnLockUndoNoModifiedPosition() 170 { 171 m_bLockUndoNoModifiedPosition = false; 172 } 173 174 175 SwUndo* UndoManager::GetLastUndo() 176 { 177 if (!SdrUndoManager::GetUndoActionCount(CurrentLevel)) 178 { 179 return 0; 180 } 181 SfxUndoAction *const pAction( SdrUndoManager::GetUndoAction(0) ); 182 return dynamic_cast<SwUndo*>(pAction); 183 } 184 185 void UndoManager::AppendUndo(SwUndo *const pUndo) 186 { 187 AddUndoAction(pUndo); 188 } 189 190 void UndoManager::ClearRedo() 191 { 192 return SdrUndoManager::ImplClearRedo_NoLock(TopLevel); 193 } 194 195 void UndoManager::DelAllUndoObj() 196 { 197 ::sw::UndoGuard const undoGuard(*this); 198 199 SdrUndoManager::ClearAllLevels(); 200 201 m_UndoSaveMark = MARK_INVALID; 202 } 203 204 205 /**************** UNDO ******************/ 206 207 SwUndoId 208 UndoManager::StartUndo(SwUndoId const i_eUndoId, 209 SwRewriter const*const pRewriter) 210 { 211 if (!IsUndoEnabled()) 212 { 213 return UNDO_EMPTY; 214 } 215 216 SwUndoId const eUndoId( (0 == i_eUndoId) ? UNDO_START : i_eUndoId ); 217 218 OSL_ASSERT(UNDO_END != eUndoId); 219 String comment( (UNDO_START == eUndoId) 220 ? String("??", RTL_TEXTENCODING_ASCII_US) 221 : String(SW_RES(UNDO_BASE + eUndoId)) ); 222 if (pRewriter) 223 { 224 OSL_ASSERT(UNDO_START != eUndoId); 225 comment = pRewriter->Apply(comment); 226 } 227 228 SdrUndoManager::EnterListAction(comment, comment, eUndoId); 229 230 return eUndoId; 231 } 232 233 234 SwUndoId 235 UndoManager::EndUndo(SwUndoId const i_eUndoId, SwRewriter const*const pRewriter) 236 { 237 if (!IsUndoEnabled()) 238 { 239 return UNDO_EMPTY; 240 } 241 242 SwUndoId const eUndoId( ((0 == i_eUndoId) || (UNDO_START == i_eUndoId)) 243 ? UNDO_END : i_eUndoId ); 244 OSL_ENSURE(!((UNDO_END == eUndoId) && pRewriter), 245 "EndUndo(): no Undo ID, but rewriter given?"); 246 247 SfxUndoAction *const pLastUndo( 248 (0 == SdrUndoManager::GetUndoActionCount(CurrentLevel)) 249 ? 0 : SdrUndoManager::GetUndoAction(0) ); 250 251 int const nCount = LeaveListAction(); 252 253 if (nCount) // otherwise: empty list action not inserted! 254 { 255 OSL_ASSERT(pLastUndo); 256 OSL_ASSERT(UNDO_START != eUndoId); 257 SfxUndoAction *const pUndoAction(SdrUndoManager::GetUndoAction(0)); 258 SfxListUndoAction *const pListAction( 259 dynamic_cast<SfxListUndoAction*>(pUndoAction)); 260 OSL_ASSERT(pListAction); 261 if (pListAction) 262 { 263 if (UNDO_END != eUndoId) 264 { 265 OSL_ENSURE(pListAction->GetId() == eUndoId, 266 "EndUndo(): given ID different from StartUndo()"); 267 // comment set by caller of EndUndo 268 String comment = String(SW_RES(UNDO_BASE + eUndoId)); 269 if (pRewriter) 270 { 271 comment = pRewriter->Apply(comment); 272 } 273 pListAction->SetComment(comment); 274 } 275 else if ((UNDO_START != pListAction->GetId())) 276 { 277 // comment set by caller of StartUndo: nothing to do here 278 } 279 else if (pLastUndo) 280 { 281 // comment was not set at StartUndo or EndUndo: 282 // take comment of last contained action 283 // (note that this works recursively, i.e. the last contained 284 // action may be a list action created by StartUndo/EndUndo) 285 String const comment(pLastUndo->GetComment()); 286 pListAction->SetComment(comment); 287 } 288 else 289 { 290 OSL_ENSURE(false, "EndUndo(): no comment?"); 291 } 292 } 293 } 294 295 return eUndoId; 296 } 297 298 bool 299 UndoManager::GetLastUndoInfo( 300 ::rtl::OUString *const o_pStr, SwUndoId *const o_pId) const 301 { 302 // this is actually expected to work on the current level, 303 // but that was really not obvious from the previous implementation... 304 if (!SdrUndoManager::GetUndoActionCount(CurrentLevel)) 305 { 306 return false; 307 } 308 309 SfxUndoAction *const pAction( SdrUndoManager::GetUndoAction(0) ); 310 311 if (o_pStr) 312 { 313 *o_pStr = pAction->GetComment(); 314 } 315 if (o_pId) 316 { 317 sal_uInt16 const nId(pAction->GetId()); 318 *o_pId = static_cast<SwUndoId>(nId); 319 } 320 321 return true; 322 } 323 324 SwUndoComments_t UndoManager::GetUndoComments() const 325 { 326 OSL_ENSURE(!SdrUndoManager::IsInListAction(), 327 "GetUndoComments() called while in list action?"); 328 329 SwUndoComments_t ret; 330 sal_uInt16 const nUndoCount(SdrUndoManager::GetUndoActionCount(TopLevel)); 331 for (sal_uInt16 n = 0; n < nUndoCount; ++n) 332 { 333 ::rtl::OUString const comment( 334 SdrUndoManager::GetUndoActionComment(n, TopLevel)); 335 ret.push_back(comment); 336 } 337 338 return ret; 339 } 340 341 342 /**************** REDO ******************/ 343 344 bool UndoManager::GetFirstRedoInfo(::rtl::OUString *const o_pStr, 345 SwUndoId *const o_pId) const 346 { 347 if (!SdrUndoManager::GetRedoActionCount(CurrentLevel)) 348 { 349 return false; 350 } 351 352 SfxUndoAction *const pAction( SdrUndoManager::GetRedoAction(0, CurrentLevel) ); 353 if ( pAction == NULL ) 354 { 355 return false; 356 } 357 358 if (o_pStr) 359 { 360 *o_pStr = pAction->GetComment(); 361 } 362 if (o_pId) 363 { 364 sal_uInt16 const nId(pAction->GetId()); 365 *o_pId = static_cast<SwUndoId>(nId); 366 } 367 368 return true; 369 } 370 371 372 SwUndoComments_t UndoManager::GetRedoComments() const 373 { 374 OSL_ENSURE(!SdrUndoManager::IsInListAction(), 375 "GetRedoComments() called while in list action?"); 376 377 SwUndoComments_t ret; 378 sal_uInt16 const nRedoCount(SdrUndoManager::GetRedoActionCount(TopLevel)); 379 for (sal_uInt16 n = 0; n < nRedoCount; ++n) 380 { 381 ::rtl::OUString const comment( 382 SdrUndoManager::GetRedoActionComment(n, TopLevel)); 383 ret.push_back(comment); 384 } 385 386 return ret; 387 } 388 389 /**************** REPEAT ******************/ 390 391 SwUndoId UndoManager::GetRepeatInfo(::rtl::OUString *const o_pStr) const 392 { 393 SwUndoId nRepeatId(UNDO_EMPTY); 394 GetLastUndoInfo(o_pStr, & nRepeatId); 395 if( REPEAT_START <= nRepeatId && REPEAT_END > nRepeatId ) 396 { 397 return nRepeatId; 398 } 399 if (o_pStr) // not repeatable -> clear comment 400 { 401 *o_pStr = String(); 402 } 403 return UNDO_EMPTY; 404 } 405 406 SwUndo * UndoManager::RemoveLastUndo() 407 { 408 if (SdrUndoManager::GetRedoActionCount(CurrentLevel) || 409 SdrUndoManager::GetRedoActionCount(TopLevel)) 410 { 411 OSL_ENSURE(false, "RemoveLastUndoAction(): there are Redo actions?"); 412 return 0; 413 } 414 if (!SdrUndoManager::GetUndoActionCount(CurrentLevel)) 415 { 416 OSL_ENSURE(false, "RemoveLastUndoAction(): no Undo actions"); 417 return 0; 418 } 419 SfxUndoAction *const pLastUndo(GetUndoAction(0)); 420 SdrUndoManager::RemoveLastUndoAction(); 421 return dynamic_cast<SwUndo *>(pLastUndo); 422 } 423 424 // svl::IUndoManager ///////////////////////////////////////////////////// 425 426 void UndoManager::EnableUndo(bool bEnable) 427 { 428 // SdrUndoManager does not have a counter anymore, but reverted to the old behavior of 429 // having a simple boolean flag for locking. So, simply forward. 430 SdrUndoManager::EnableUndo(bEnable); 431 } 432 433 void UndoManager::AddUndoAction(SfxUndoAction *pAction, sal_Bool bTryMerge) 434 { 435 SwUndo *const pUndo( dynamic_cast<SwUndo *>(pAction) ); 436 if (pUndo) 437 { 438 if (nsRedlineMode_t::REDLINE_NONE == pUndo->GetRedlineMode()) 439 { 440 pUndo->SetRedlineMode( m_rRedlineAccess.GetRedlineMode() ); 441 } 442 } 443 SdrUndoManager::AddUndoAction(pAction, bTryMerge); 444 // if the undo nodes array is too large, delete some actions 445 while (UNDO_ACTION_LIMIT < GetUndoNodes().Count()) 446 { 447 RemoveOldestUndoActions(1); 448 } 449 } 450 451 class CursorGuard 452 { 453 public: 454 CursorGuard(SwEditShell & rShell, bool const bSave) 455 : m_rShell(rShell) 456 , m_bSaveCursor(bSave) 457 { 458 if (m_bSaveCursor) 459 { 460 m_rShell.Push(); // prevent modification of current cursor 461 } 462 } 463 ~CursorGuard() 464 { 465 if (m_bSaveCursor) 466 { 467 m_rShell.Pop( sal_False ); 468 } 469 } 470 private: 471 SwEditShell & m_rShell; 472 bool const m_bSaveCursor; 473 }; 474 475 bool UndoManager::impl_DoUndoRedo(UndoOrRedo_t const undoOrRedo) 476 { 477 SwDoc & rDoc(*GetUndoNodes().GetDoc()); 478 479 UnoActionContext c(& rDoc); // exception-safe StartAllAction/EndAllAction 480 481 SwEditShell *const pEditShell( rDoc.GetEditShell() ); 482 483 OSL_ENSURE(pEditShell, "sw::UndoManager needs a SwEditShell!"); 484 if (!pEditShell) 485 { 486 throw uno::RuntimeException(); 487 } 488 489 // in case the model has controllers locked, the Undo should not 490 // change the view cursors! 491 bool const bSaveCursors(pEditShell->CursorsLocked()); 492 CursorGuard aCursorGuard(*pEditShell, bSaveCursors); 493 if (!bSaveCursors) 494 { 495 // (in case Undo was called via API) clear the cursors: 496 pEditShell->KillPams(); 497 pEditShell->SetMark(); 498 pEditShell->ClearMark(); 499 } 500 501 bool bRet(false); 502 503 ::sw::UndoRedoContext context(rDoc, *pEditShell); 504 505 // N.B. these may throw! 506 if (UNDO == undoOrRedo) 507 { 508 bRet = SdrUndoManager::UndoWithContext(context); 509 } 510 else 511 { 512 bRet = SdrUndoManager::RedoWithContext(context); 513 } 514 515 if (bRet) 516 { 517 // if we are at the "last save" position, the document is not modified 518 if (SdrUndoManager::HasTopUndoActionMark(m_UndoSaveMark)) 519 { 520 m_rState.ResetModified(); 521 } 522 else 523 { 524 m_rState.SetModified(); 525 } 526 } 527 528 pEditShell->HandleUndoRedoContext(context); 529 530 return bRet; 531 } 532 533 sal_Bool UndoManager::Undo() 534 { 535 if(isTextEditActive()) 536 { 537 return SdrUndoManager::Undo(); 538 } 539 else 540 { 541 return impl_DoUndoRedo(UNDO); 542 } 543 } 544 545 sal_Bool UndoManager::Redo() 546 { 547 if(isTextEditActive()) 548 { 549 return SdrUndoManager::Redo(); 550 } 551 else 552 { 553 return impl_DoUndoRedo(REDO); 554 } 555 } 556 557 /** N.B.: this does _not_ call SdrUndoManager::Repeat because it is not 558 possible to wrap a list action around it: 559 calling EnterListAction here will cause SdrUndoManager::Repeat 560 to repeat the list action! 561 */ 562 bool 563 UndoManager::Repeat(::sw::RepeatContext & rContext, 564 sal_uInt16 const nRepeatCount) 565 { 566 if (SdrUndoManager::IsInListAction()) 567 { 568 OSL_ENSURE(false, "repeat in open list action???"); 569 return false; 570 } 571 if (!SdrUndoManager::GetUndoActionCount(TopLevel)) 572 { 573 return false; 574 } 575 SfxUndoAction *const pRepeatAction(GetUndoAction(0)); 576 OSL_ASSERT(pRepeatAction); 577 if (!pRepeatAction || !pRepeatAction->CanRepeat(rContext)) 578 { 579 return false; 580 } 581 582 ::rtl::OUString const comment(pRepeatAction->GetComment()); 583 ::rtl::OUString const rcomment(pRepeatAction->GetRepeatComment(rContext)); 584 sal_uInt16 const nId(pRepeatAction->GetId()); 585 if (DoesUndo()) 586 { 587 EnterListAction(comment, rcomment, nId); 588 } 589 590 SwPaM *const pFirstCursor(& rContext.GetRepeatPaM()); 591 do { // iterate over ring 592 for (sal_uInt16 nRptCnt = nRepeatCount; nRptCnt > 0; --nRptCnt) 593 { 594 pRepeatAction->Repeat(rContext); 595 } 596 rContext.m_bDeleteRepeated = false; // reset for next PaM 597 rContext.m_pCurrentPaM = 598 static_cast<SwPaM*>(rContext.m_pCurrentPaM->GetNext()); 599 } while (pFirstCursor != & rContext.GetRepeatPaM()); 600 601 if (DoesUndo()) 602 { 603 LeaveListAction(); 604 } 605 return true; 606 } 607 608 } // namespace sw 609 610