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