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