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