update credits
[LibreOffice.git] / sw / source / core / undo / docundo.cxx
blobf38cd4c47528f7a2c89d35a69998cc5bf0d1484d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <UndoManager.hxx>
23 #include <vcl/wrkwin.hxx>
25 #include <svx/svdmodel.hxx>
27 #include <swmodule.hxx>
28 #include <doc.hxx>
29 #include <ndarr.hxx>
30 #include <pam.hxx>
31 #include <ndtxt.hxx>
32 #include <swundo.hxx>
33 #include <UndoCore.hxx>
34 #include <rolbck.hxx>
35 #include <undo.hrc>
36 #include <editsh.hxx>
37 #include <unobaseclass.hxx>
38 #include <limits>
40 using namespace ::com::sun::star;
43 // the undo array should never grow beyond this limit:
44 #define UNDO_ACTION_LIMIT (USHRT_MAX - 1000)
47 // UndoManager ///////////////////////////////////////////////////////////
49 namespace sw {
51 SAL_WNODEPRECATED_DECLARATIONS_PUSH
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)
65 OSL_ASSERT(m_pUndoNodes.get());
66 // writer expects it to be disabled initially
67 // Undo is enabled by SwEditShell constructor
68 SfxUndoManager::EnableUndo(false);
70 SAL_WNODEPRECATED_DECLARATIONS_POP
72 SwNodes const& UndoManager::GetUndoNodes() const
74 return *m_pUndoNodes;
77 SwNodes & UndoManager::GetUndoNodes()
79 return *m_pUndoNodes;
82 bool UndoManager::IsUndoNodes(SwNodes const& rNodes) const
84 return & rNodes == m_pUndoNodes.get();
87 void UndoManager::DoUndo(bool const bDoUndo)
89 EnableUndo(bDoUndo);
91 SdrModel *const pSdrModel = m_rDrawModelAccess.GetDrawModel();
92 if( pSdrModel )
94 pSdrModel->EnableUndo(bDoUndo);
98 bool UndoManager::DoesUndo() const
100 return IsUndoEnabled();
103 void UndoManager::DoGroupUndo(bool const bDoUndo)
105 m_bGroupUndo = bDoUndo;
108 bool UndoManager::DoesGroupUndo() const
110 return m_bGroupUndo;
113 void UndoManager::DoDrawUndo(bool const bDoUndo)
115 m_bDrawUndo = bDoUndo;
118 bool UndoManager::DoesDrawUndo() const
120 return m_bDrawUndo;
124 bool UndoManager::IsUndoNoResetModified() const
126 return MARK_INVALID == m_UndoSaveMark;
129 void UndoManager::SetUndoNoResetModified()
131 if (MARK_INVALID != m_UndoSaveMark)
133 RemoveMark(m_UndoSaveMark);
134 m_UndoSaveMark = MARK_INVALID;
138 void UndoManager::SetUndoNoModifiedPosition()
140 if (!m_bLockUndoNoModifiedPosition)
142 m_UndoSaveMark = MarkTopUndoAction();
146 void UndoManager::LockUndoNoModifiedPosition()
148 m_bLockUndoNoModifiedPosition = true;
151 void UndoManager::UnLockUndoNoModifiedPosition()
153 m_bLockUndoNoModifiedPosition = false;
157 SwUndo* UndoManager::GetLastUndo()
159 if (!SfxUndoManager::GetUndoActionCount(CurrentLevel))
161 return 0;
163 SfxUndoAction *const pAction( SfxUndoManager::GetUndoAction(0) );
164 return dynamic_cast<SwUndo*>(pAction);
167 void UndoManager::AppendUndo(SwUndo *const pUndo)
169 AddUndoAction(pUndo);
172 void UndoManager::ClearRedo()
174 return SfxUndoManager::ImplClearRedo_NoLock(TopLevel);
177 void UndoManager::DelAllUndoObj()
179 ::sw::UndoGuard const undoGuard(*this);
181 SfxUndoManager::ClearAllLevels();
183 m_UndoSaveMark = MARK_INVALID;
187 /**************** UNDO ******************/
189 SwUndoId
190 UndoManager::StartUndo(SwUndoId const i_eUndoId,
191 SwRewriter const*const pRewriter)
193 if (!IsUndoEnabled())
195 return UNDO_EMPTY;
198 SwUndoId const eUndoId( (0 == i_eUndoId) ? UNDO_START : i_eUndoId );
200 OSL_ASSERT(UNDO_END != eUndoId);
201 String comment( (UNDO_START == eUndoId)
202 ? String("??", RTL_TEXTENCODING_ASCII_US)
203 : String(SW_RES(UNDO_BASE + eUndoId)) );
204 if (pRewriter)
206 OSL_ASSERT(UNDO_START != eUndoId);
207 comment = pRewriter->Apply(comment);
210 SfxUndoManager::EnterListAction(comment, comment, eUndoId);
212 return eUndoId;
216 SwUndoId
217 UndoManager::EndUndo(SwUndoId const i_eUndoId, SwRewriter const*const pRewriter)
219 if (!IsUndoEnabled())
221 return UNDO_EMPTY;
224 SwUndoId const eUndoId( ((0 == i_eUndoId) || (UNDO_START == i_eUndoId))
225 ? UNDO_END : i_eUndoId );
226 OSL_ENSURE(!((UNDO_END == eUndoId) && pRewriter),
227 "EndUndo(): no Undo ID, but rewriter given?");
229 SfxUndoAction *const pLastUndo(
230 (0 == SfxUndoManager::GetUndoActionCount(CurrentLevel))
231 ? 0 : SfxUndoManager::GetUndoAction(0) );
233 int const nCount = LeaveListAction();
235 if (nCount) // otherwise: empty list action not inserted!
237 OSL_ASSERT(pLastUndo);
238 OSL_ASSERT(UNDO_START != eUndoId);
239 SfxUndoAction *const pUndoAction(SfxUndoManager::GetUndoAction(0));
240 SfxListUndoAction *const pListAction(
241 dynamic_cast<SfxListUndoAction*>(pUndoAction));
242 OSL_ASSERT(pListAction);
243 if (pListAction)
245 if (UNDO_END != eUndoId)
247 OSL_ENSURE(pListAction->GetId() == eUndoId,
248 "EndUndo(): given ID different from StartUndo()");
249 // comment set by caller of EndUndo
250 String comment = String(SW_RES(UNDO_BASE + eUndoId));
251 if (pRewriter)
253 comment = pRewriter->Apply(comment);
255 pListAction->SetComment(comment);
257 else if ((UNDO_START != pListAction->GetId()))
259 // comment set by caller of StartUndo: nothing to do here
261 else if (pLastUndo)
263 // comment was not set at StartUndo or EndUndo:
264 // take comment of last contained action
265 // (note that this works recursively, i.e. the last contained
266 // action may be a list action created by StartUndo/EndUndo)
267 String const comment(pLastUndo->GetComment());
268 pListAction->SetComment(comment);
270 else
272 OSL_ENSURE(false, "EndUndo(): no comment?");
277 return eUndoId;
280 bool
281 UndoManager::GetLastUndoInfo(
282 OUString *const o_pStr, SwUndoId *const o_pId) const
284 // this is actually expected to work on the current level,
285 // but that was really not obvious from the previous implementation...
286 if (!SfxUndoManager::GetUndoActionCount(CurrentLevel))
288 return false;
291 SfxUndoAction *const pAction( SfxUndoManager::GetUndoAction(0) );
292 if (o_pStr)
294 *o_pStr = pAction->GetComment();
296 if (o_pId)
298 sal_uInt16 const nId(pAction->GetId());
299 *o_pId = static_cast<SwUndoId>(nId);
302 return true;
305 SwUndoComments_t UndoManager::GetUndoComments() const
307 OSL_ENSURE(!SfxUndoManager::IsInListAction(),
308 "GetUndoComments() called while in list action?");
310 SwUndoComments_t ret;
311 sal_uInt16 const nUndoCount(SfxUndoManager::GetUndoActionCount(TopLevel));
312 for (sal_uInt16 n = 0; n < nUndoCount; ++n)
314 OUString const comment(
315 SfxUndoManager::GetUndoActionComment(n, TopLevel));
316 ret.push_back(comment);
319 return ret;
323 /**************** REDO ******************/
324 bool UndoManager::GetFirstRedoInfo(OUString *const o_pStr) const
326 if (!SfxUndoManager::GetRedoActionCount(CurrentLevel))
328 return false;
331 if (o_pStr)
333 *o_pStr = SfxUndoManager::GetRedoActionComment(0, CurrentLevel);
336 return true;
340 SwUndoComments_t UndoManager::GetRedoComments() const
342 OSL_ENSURE(!SfxUndoManager::IsInListAction(),
343 "GetRedoComments() called while in list action?");
345 SwUndoComments_t ret;
346 sal_uInt16 const nRedoCount(SfxUndoManager::GetRedoActionCount(TopLevel));
347 for (sal_uInt16 n = 0; n < nRedoCount; ++n)
349 OUString const comment(
350 SfxUndoManager::GetRedoActionComment(n, TopLevel));
351 ret.push_back(comment);
354 return ret;
357 /**************** REPEAT ******************/
359 SwUndoId UndoManager::GetRepeatInfo(OUString *const o_pStr) const
361 SwUndoId nRepeatId(UNDO_EMPTY);
362 GetLastUndoInfo(o_pStr, & nRepeatId);
363 if( REPEAT_START <= nRepeatId && REPEAT_END > nRepeatId )
365 return nRepeatId;
367 if (o_pStr) // not repeatable -> clear comment
369 *o_pStr = String();
371 return UNDO_EMPTY;
374 SwUndo * UndoManager::RemoveLastUndo()
376 if (SfxUndoManager::GetRedoActionCount(CurrentLevel) ||
377 SfxUndoManager::GetRedoActionCount(TopLevel))
379 OSL_ENSURE(false, "RemoveLastUndoAction(): there are Redo actions?");
380 return 0;
382 if (!SfxUndoManager::GetUndoActionCount(CurrentLevel))
384 OSL_ENSURE(false, "RemoveLastUndoAction(): no Undo actions");
385 return 0;
387 SfxUndoAction *const pLastUndo(GetUndoAction(0));
388 SfxUndoManager::RemoveLastUndoAction();
389 return dynamic_cast<SwUndo *>(pLastUndo);
392 // svl::IUndoManager /////////////////////////////////////////////////////
394 void UndoManager::EnableUndo(bool bEnable)
396 // SfxUndoManager does not have a counter anymore, but reverted to the old behavior of
397 // having a simple boolean flag for locking. So, simply forward.
398 SfxUndoManager::EnableUndo(bEnable);
401 void UndoManager::AddUndoAction(SfxUndoAction *pAction, sal_Bool bTryMerge)
403 SwUndo *const pUndo( dynamic_cast<SwUndo *>(pAction) );
404 if (pUndo)
406 if (nsRedlineMode_t::REDLINE_NONE == pUndo->GetRedlineMode())
408 pUndo->SetRedlineMode( m_rRedlineAccess.GetRedlineMode() );
411 SfxUndoManager::AddUndoAction(pAction, bTryMerge);
412 // if the undo nodes array is too large, delete some actions
413 while (UNDO_ACTION_LIMIT < GetUndoNodes().Count())
415 RemoveOldestUndoActions(1);
419 class CursorGuard
421 public:
422 CursorGuard(SwEditShell & rShell, bool const bSave)
423 : m_rShell(rShell)
424 , m_bSaveCursor(bSave)
426 if (m_bSaveCursor)
428 m_rShell.Push(); // prevent modification of current cursor
431 ~CursorGuard()
433 if (m_bSaveCursor)
435 m_rShell.Pop();
438 private:
439 SwEditShell & m_rShell;
440 bool const m_bSaveCursor;
443 bool UndoManager::impl_DoUndoRedo(UndoOrRedo_t const undoOrRedo)
445 SwDoc & rDoc(*GetUndoNodes().GetDoc());
447 UnoActionContext c(& rDoc); // exception-safe StartAllAction/EndAllAction
449 SwEditShell *const pEditShell( rDoc.GetEditShell() );
451 OSL_ENSURE(pEditShell, "sw::UndoManager needs a SwEditShell!");
452 if (!pEditShell)
454 throw uno::RuntimeException();
457 // in case the model has controllers locked, the Undo should not
458 // change the view cursors!
459 bool const bSaveCursors(pEditShell->CursorsLocked());
460 CursorGuard(*pEditShell, bSaveCursors);
461 if (!bSaveCursors)
463 // (in case Undo was called via API) clear the cursors:
464 pEditShell->KillPams();
465 pEditShell->SetMark();
466 pEditShell->ClearMark();
469 bool bRet(false);
471 ::sw::UndoRedoContext context(rDoc, *pEditShell);
473 // N.B. these may throw!
474 if (UNDO == undoOrRedo)
476 bRet = SfxUndoManager::UndoWithContext(context);
478 else
480 bRet = SfxUndoManager::RedoWithContext(context);
483 if (bRet)
485 // if we are at the "last save" position, the document is not modified
486 if (SfxUndoManager::HasTopUndoActionMark(m_UndoSaveMark))
488 m_rState.ResetModified();
490 else
492 m_rState.SetModified();
496 pEditShell->HandleUndoRedoContext(context);
498 return bRet;
501 sal_Bool UndoManager::Undo()
503 bool const bRet = impl_DoUndoRedo(UNDO);
504 return bRet;
507 sal_Bool UndoManager::Redo()
509 bool const bRet = impl_DoUndoRedo(REDO);
510 return bRet;
513 /** N.B.: this does _not_ call SfxUndoManager::Repeat because it is not
514 possible to wrap a list action around it:
515 calling EnterListAction here will cause SfxUndoManager::Repeat
516 to repeat the list action!
518 bool
519 UndoManager::Repeat(::sw::RepeatContext & rContext,
520 sal_uInt16 const nRepeatCount)
522 if (SfxUndoManager::IsInListAction())
524 OSL_ENSURE(false, "repeat in open list action???");
525 return false;
527 if (!SfxUndoManager::GetUndoActionCount(TopLevel))
529 return false;
531 SfxUndoAction *const pRepeatAction(GetUndoAction(0));
532 OSL_ASSERT(pRepeatAction);
533 if (!pRepeatAction || !pRepeatAction->CanRepeat(rContext))
535 return false;
538 OUString const comment(pRepeatAction->GetComment());
539 OUString const rcomment(pRepeatAction->GetRepeatComment(rContext));
540 sal_uInt16 const nId(pRepeatAction->GetId());
541 if (DoesUndo())
543 EnterListAction(comment, rcomment, nId);
546 SwPaM *const pFirstCursor(& rContext.GetRepeatPaM());
547 do { // iterate over ring
548 for (sal_uInt16 nRptCnt = nRepeatCount; nRptCnt > 0; --nRptCnt)
550 pRepeatAction->Repeat(rContext);
552 rContext.m_bDeleteRepeated = false; // reset for next PaM
553 rContext.m_pCurrentPaM =
554 static_cast<SwPaM*>(rContext.m_pCurrentPaM->GetNext());
555 } while (pFirstCursor != & rContext.GetRepeatPaM());
557 if (DoesUndo())
559 LeaveListAction();
561 return true;
564 } // namespace sw
566 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */