1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
33 #include <UndoCore.hxx>
37 #include <unobaseclass.hxx>
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 ///////////////////////////////////////////////////////////
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
)
59 , m_pUndoNodes(pUndoNodes
)
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
77 SwNodes
& UndoManager::GetUndoNodes()
82 bool UndoManager::IsUndoNodes(SwNodes
const& rNodes
) const
84 return & rNodes
== m_pUndoNodes
.get();
87 void UndoManager::DoUndo(bool const bDoUndo
)
91 SdrModel
*const pSdrModel
= m_rDrawModelAccess
.GetDrawModel();
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
113 void UndoManager::DoDrawUndo(bool const bDoUndo
)
115 m_bDrawUndo
= bDoUndo
;
118 bool UndoManager::DoesDrawUndo() const
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
))
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 ******************/
190 UndoManager::StartUndo(SwUndoId
const i_eUndoId
,
191 SwRewriter
const*const pRewriter
)
193 if (!IsUndoEnabled())
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
)) );
206 OSL_ASSERT(UNDO_START
!= eUndoId
);
207 comment
= pRewriter
->Apply(comment
);
210 SfxUndoManager::EnterListAction(comment
, comment
, eUndoId
);
217 UndoManager::EndUndo(SwUndoId
const i_eUndoId
, SwRewriter
const*const pRewriter
)
219 if (!IsUndoEnabled())
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
);
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
));
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
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
);
272 OSL_ENSURE(false, "EndUndo(): no comment?");
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
))
291 SfxUndoAction
*const pAction( SfxUndoManager::GetUndoAction(0) );
294 *o_pStr
= pAction
->GetComment();
298 sal_uInt16
const nId(pAction
->GetId());
299 *o_pId
= static_cast<SwUndoId
>(nId
);
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
);
323 /**************** REDO ******************/
324 bool UndoManager::GetFirstRedoInfo(OUString
*const o_pStr
) const
326 if (!SfxUndoManager::GetRedoActionCount(CurrentLevel
))
333 *o_pStr
= SfxUndoManager::GetRedoActionComment(0, CurrentLevel
);
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
);
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
)
367 if (o_pStr
) // not repeatable -> clear comment
374 SwUndo
* UndoManager::RemoveLastUndo()
376 if (SfxUndoManager::GetRedoActionCount(CurrentLevel
) ||
377 SfxUndoManager::GetRedoActionCount(TopLevel
))
379 OSL_ENSURE(false, "RemoveLastUndoAction(): there are Redo actions?");
382 if (!SfxUndoManager::GetUndoActionCount(CurrentLevel
))
384 OSL_ENSURE(false, "RemoveLastUndoAction(): no Undo actions");
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
) );
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);
422 CursorGuard(SwEditShell
& rShell
, bool const bSave
)
424 , m_bSaveCursor(bSave
)
428 m_rShell
.Push(); // prevent modification of current cursor
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!");
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
);
463 // (in case Undo was called via API) clear the cursors:
464 pEditShell
->KillPams();
465 pEditShell
->SetMark();
466 pEditShell
->ClearMark();
471 ::sw::UndoRedoContext
context(rDoc
, *pEditShell
);
473 // N.B. these may throw!
474 if (UNDO
== undoOrRedo
)
476 bRet
= SfxUndoManager::UndoWithContext(context
);
480 bRet
= SfxUndoManager::RedoWithContext(context
);
485 // if we are at the "last save" position, the document is not modified
486 if (SfxUndoManager::HasTopUndoActionMark(m_UndoSaveMark
))
488 m_rState
.ResetModified();
492 m_rState
.SetModified();
496 pEditShell
->HandleUndoRedoContext(context
);
501 sal_Bool
UndoManager::Undo()
503 bool const bRet
= impl_DoUndoRedo(UNDO
);
507 sal_Bool
UndoManager::Redo()
509 bool const bRet
= impl_DoUndoRedo(REDO
);
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!
519 UndoManager::Repeat(::sw::RepeatContext
& rContext
,
520 sal_uInt16
const nRepeatCount
)
522 if (SfxUndoManager::IsInListAction())
524 OSL_ENSURE(false, "repeat in open list action???");
527 if (!SfxUndoManager::GetUndoActionCount(TopLevel
))
531 SfxUndoAction
*const pRepeatAction(GetUndoAction(0));
532 OSL_ASSERT(pRepeatAction
);
533 if (!pRepeatAction
|| !pRepeatAction
->CanRepeat(rContext
))
538 OUString
const comment(pRepeatAction
->GetComment());
539 OUString
const rcomment(pRepeatAction
->GetRepeatComment(rContext
));
540 sal_uInt16
const nId(pRepeatAction
->GetId());
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());
566 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */