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
UndoManager(::std::auto_ptr<SwNodes> pUndoNodes,IDocumentDrawModelAccess & rDrawModelAccess,IDocumentRedlineAccess & rRedlineAccess,IDocumentState & rState)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
GetUndoNodes() const80 SwNodes const& UndoManager::GetUndoNodes() const
81 {
82 return *m_pUndoNodes;
83 }
84
GetUndoNodes()85 SwNodes & UndoManager::GetUndoNodes()
86 {
87 return *m_pUndoNodes;
88 }
89
IsUndoNodes(SwNodes const & rNodes) const90 bool UndoManager::IsUndoNodes(SwNodes const& rNodes) const
91 {
92 return & rNodes == m_pUndoNodes.get();
93 }
94
DoUndo(bool const bDoUndo)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
DoesUndo() const109 bool UndoManager::DoesUndo() const
110 {
111 if(isTextEditActive())
112 {
113 return false;
114 }
115 else
116 {
117 return IsUndoEnabled();
118 }
119 }
120
DoGroupUndo(bool const bDoUndo)121 void UndoManager::DoGroupUndo(bool const bDoUndo)
122 {
123 m_bGroupUndo = bDoUndo;
124 }
125
DoesGroupUndo() const126 bool UndoManager::DoesGroupUndo() const
127 {
128 return m_bGroupUndo;
129 }
130
DoDrawUndo(bool const bDoUndo)131 void UndoManager::DoDrawUndo(bool const bDoUndo)
132 {
133 m_bDrawUndo = bDoUndo;
134 }
135
DoesDrawUndo() const136 bool UndoManager::DoesDrawUndo() const
137 {
138 return m_bDrawUndo;
139 }
140
141
IsUndoNoResetModified() const142 bool UndoManager::IsUndoNoResetModified() const
143 {
144 return MARK_INVALID == m_UndoSaveMark;
145 }
146
SetUndoNoResetModified()147 void UndoManager::SetUndoNoResetModified()
148 {
149 if (MARK_INVALID != m_UndoSaveMark)
150 {
151 RemoveMark(m_UndoSaveMark);
152 m_UndoSaveMark = MARK_INVALID;
153 }
154 }
155
SetUndoNoModifiedPosition()156 void UndoManager::SetUndoNoModifiedPosition()
157 {
158 if (!m_bLockUndoNoModifiedPosition)
159 {
160 m_UndoSaveMark = MarkTopUndoAction();
161 }
162 }
163
LockUndoNoModifiedPosition()164 void UndoManager::LockUndoNoModifiedPosition()
165 {
166 m_bLockUndoNoModifiedPosition = true;
167 }
168
UnLockUndoNoModifiedPosition()169 void UndoManager::UnLockUndoNoModifiedPosition()
170 {
171 m_bLockUndoNoModifiedPosition = false;
172 }
173
174
GetLastUndo()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
AppendUndo(SwUndo * const pUndo)185 void UndoManager::AppendUndo(SwUndo *const pUndo)
186 {
187 AddUndoAction(pUndo);
188 }
189
ClearRedo()190 void UndoManager::ClearRedo()
191 {
192 return SdrUndoManager::ImplClearRedo_NoLock(TopLevel);
193 }
194
DelAllUndoObj()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
StartUndo(SwUndoId const i_eUndoId,SwRewriter const * const pRewriter)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
EndUndo(SwUndoId const i_eUndoId,SwRewriter const * const pRewriter)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
GetLastUndoInfo(::rtl::OUString * const o_pStr,SwUndoId * const o_pId) const299 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
GetUndoComments() const324 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
GetFirstRedoInfo(::rtl::OUString * const o_pStr,SwUndoId * const o_pId) const344 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
GetRedoComments() const372 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
GetRepeatInfo(::rtl::OUString * const o_pStr) const391 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
RemoveLastUndo()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
EnableUndo(bool bEnable)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
AddUndoAction(SfxUndoAction * pAction,sal_Bool bTryMerge)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:
CursorGuard(SwEditShell & rShell,bool const bSave)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 }
~CursorGuard()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
impl_DoUndoRedo(UndoOrRedo_t const undoOrRedo)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
Undo()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
Redo()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
Repeat(::sw::RepeatContext & rContext,sal_uInt16 const nRepeatCount)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