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