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