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