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