xref: /trunk/main/sw/source/core/undo/docundo.cxx (revision 89358e0f)
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,
345                                    SwUndoId *const o_pId) const
346 {
347     if (!SdrUndoManager::GetRedoActionCount(CurrentLevel))
348     {
349         return false;
350     }
351 
352     SfxUndoAction *const pAction( SdrUndoManager::GetRedoAction(0, CurrentLevel) );
353     if ( pAction == NULL )
354     {
355         return false;
356     }
357 
358     if (o_pStr)
359     {
360         *o_pStr = pAction->GetComment();
361     }
362     if (o_pId)
363     {
364         sal_uInt16 const nId(pAction->GetId());
365         *o_pId = static_cast<SwUndoId>(nId);
366     }
367 
368     return true;
369 }
370 
371 
372 SwUndoComments_t UndoManager::GetRedoComments() const
373 {
374     OSL_ENSURE(!SdrUndoManager::IsInListAction(),
375             "GetRedoComments() called while in list action?");
376 
377     SwUndoComments_t ret;
378     sal_uInt16 const nRedoCount(SdrUndoManager::GetRedoActionCount(TopLevel));
379     for (sal_uInt16 n = 0; n < nRedoCount; ++n)
380     {
381         ::rtl::OUString const comment(
382                 SdrUndoManager::GetRedoActionComment(n, TopLevel));
383         ret.push_back(comment);
384     }
385 
386     return ret;
387 }
388 
389 /**************** REPEAT ******************/
390 
391 SwUndoId UndoManager::GetRepeatInfo(::rtl::OUString *const o_pStr) const
392 {
393     SwUndoId nRepeatId(UNDO_EMPTY);
394     GetLastUndoInfo(o_pStr, & nRepeatId);
395 	if( REPEAT_START <= nRepeatId && REPEAT_END > nRepeatId )
396     {
397 		return nRepeatId;
398     }
399     if (o_pStr) // not repeatable -> clear comment
400     {
401         *o_pStr = String();
402     }
403     return UNDO_EMPTY;
404 }
405 
406 SwUndo * UndoManager::RemoveLastUndo()
407 {
408     if (SdrUndoManager::GetRedoActionCount(CurrentLevel) ||
409         SdrUndoManager::GetRedoActionCount(TopLevel))
410     {
411         OSL_ENSURE(false, "RemoveLastUndoAction(): there are Redo actions?");
412         return 0;
413     }
414     if (!SdrUndoManager::GetUndoActionCount(CurrentLevel))
415     {
416         OSL_ENSURE(false, "RemoveLastUndoAction(): no Undo actions");
417         return 0;
418     }
419     SfxUndoAction *const pLastUndo(GetUndoAction(0));
420     SdrUndoManager::RemoveLastUndoAction();
421     return dynamic_cast<SwUndo *>(pLastUndo);
422 }
423 
424 // svl::IUndoManager /////////////////////////////////////////////////////
425 
426 void UndoManager::EnableUndo(bool bEnable)
427 {
428     // SdrUndoManager does not have a counter anymore, but reverted to the old behavior of
429     // having a simple boolean flag for locking. So, simply forward.
430     SdrUndoManager::EnableUndo(bEnable);
431 }
432 
433 void UndoManager::AddUndoAction(SfxUndoAction *pAction, sal_Bool bTryMerge)
434 {
435     SwUndo *const pUndo( dynamic_cast<SwUndo *>(pAction) );
436     if (pUndo)
437     {
438         if (nsRedlineMode_t::REDLINE_NONE == pUndo->GetRedlineMode())
439         {
440             pUndo->SetRedlineMode( m_rRedlineAccess.GetRedlineMode() );
441         }
442     }
443     SdrUndoManager::AddUndoAction(pAction, bTryMerge);
444     // if the undo nodes array is too large, delete some actions
445     while (UNDO_ACTION_LIMIT < GetUndoNodes().Count())
446     {
447         RemoveOldestUndoActions(1);
448     }
449 }
450 
451 class CursorGuard
452 {
453 public:
454     CursorGuard(SwEditShell & rShell, bool const bSave)
455         : m_rShell(rShell)
456         , m_bSaveCursor(bSave)
457     {
458         if (m_bSaveCursor)
459         {
460             m_rShell.Push(); // prevent modification of current cursor
461         }
462     }
463     ~CursorGuard()
464     {
465         if (m_bSaveCursor)
466         {
467             m_rShell.Pop( sal_False );
468         }
469     }
470 private:
471     SwEditShell & m_rShell;
472     bool const m_bSaveCursor;
473 };
474 
475 bool UndoManager::impl_DoUndoRedo(UndoOrRedo_t const undoOrRedo)
476 {
477     SwDoc & rDoc(*GetUndoNodes().GetDoc());
478 
479     UnoActionContext c(& rDoc); // exception-safe StartAllAction/EndAllAction
480 
481     SwEditShell *const pEditShell( rDoc.GetEditShell() );
482 
483     OSL_ENSURE(pEditShell, "sw::UndoManager needs a SwEditShell!");
484     if (!pEditShell)
485     {
486         throw uno::RuntimeException();
487     }
488 
489     // in case the model has controllers locked, the Undo should not
490     // change the view cursors!
491     bool const bSaveCursors(pEditShell->CursorsLocked());
492     CursorGuard aCursorGuard(*pEditShell, bSaveCursors);
493     if (!bSaveCursors)
494     {
495         // (in case Undo was called via API) clear the cursors:
496         pEditShell->KillPams();
497         pEditShell->SetMark();
498         pEditShell->ClearMark();
499     }
500 
501     bool bRet(false);
502 
503     ::sw::UndoRedoContext context(rDoc, *pEditShell);
504 
505     // N.B. these may throw!
506     if (UNDO == undoOrRedo)
507     {
508         bRet = SdrUndoManager::UndoWithContext(context);
509     }
510     else
511     {
512         bRet = SdrUndoManager::RedoWithContext(context);
513     }
514 
515     if (bRet)
516     {
517         // if we are at the "last save" position, the document is not modified
518         if (SdrUndoManager::HasTopUndoActionMark(m_UndoSaveMark))
519         {
520             m_rState.ResetModified();
521         }
522         else
523         {
524             m_rState.SetModified();
525         }
526     }
527 
528     pEditShell->HandleUndoRedoContext(context);
529 
530     return bRet;
531 }
532 
533 sal_Bool UndoManager::Undo()
534 {
535     if(isTextEditActive())
536     {
537         return SdrUndoManager::Undo();
538     }
539     else
540     {
541         return impl_DoUndoRedo(UNDO);
542     }
543 }
544 
545 sal_Bool UndoManager::Redo()
546 {
547     if(isTextEditActive())
548     {
549         return SdrUndoManager::Redo();
550     }
551     else
552     {
553         return impl_DoUndoRedo(REDO);
554     }
555 }
556 
557 /** N.B.: this does _not_ call SdrUndoManager::Repeat because it is not
558           possible to wrap a list action around it:
559           calling EnterListAction here will cause SdrUndoManager::Repeat
560           to repeat the list action!
561  */
562 bool
563 UndoManager::Repeat(::sw::RepeatContext & rContext,
564         sal_uInt16 const nRepeatCount)
565 {
566     if (SdrUndoManager::IsInListAction())
567     {
568         OSL_ENSURE(false, "repeat in open list action???");
569         return false;
570     }
571     if (!SdrUndoManager::GetUndoActionCount(TopLevel))
572     {
573         return false;
574     }
575     SfxUndoAction *const pRepeatAction(GetUndoAction(0));
576     OSL_ASSERT(pRepeatAction);
577     if (!pRepeatAction || !pRepeatAction->CanRepeat(rContext))
578     {
579         return false;
580     }
581 
582     ::rtl::OUString const comment(pRepeatAction->GetComment());
583     ::rtl::OUString const rcomment(pRepeatAction->GetRepeatComment(rContext));
584     sal_uInt16 const nId(pRepeatAction->GetId());
585     if (DoesUndo())
586     {
587         EnterListAction(comment, rcomment, nId);
588     }
589 
590     SwPaM *const pFirstCursor(& rContext.GetRepeatPaM());
591     do {    // iterate over ring
592         for (sal_uInt16 nRptCnt = nRepeatCount; nRptCnt > 0; --nRptCnt)
593         {
594             pRepeatAction->Repeat(rContext);
595         }
596         rContext.m_bDeleteRepeated = false; // reset for next PaM
597         rContext.m_pCurrentPaM =
598             static_cast<SwPaM*>(rContext.m_pCurrentPaM->GetNext());
599     } while (pFirstCursor != & rContext.GetRepeatPaM());
600 
601     if (DoesUndo())
602     {
603         LeaveListAction();
604     }
605     return true;
606 }
607 
608 } // namespace sw
609 
610