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