1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <svl/undo.hxx>
21
22 #include <osl/mutex.hxx>
23 #include <sal/log.hxx>
24 #include <comphelper/flagguard.hxx>
25 #include <tools/diagnose_ex.h>
26 #include <tools/long.hxx>
27 #include <libxml/xmlwriter.h>
28 #include <boost/property_tree/json_parser.hpp>
29 #include <unotools/datetime.hxx>
30
31 #include <memory>
32 #include <vector>
33 #include <limits.h>
34 #include <algorithm>
35
36
~SfxRepeatTarget()37 SfxRepeatTarget::~SfxRepeatTarget()
38 {
39 }
40
41
~SfxUndoContext()42 SfxUndoContext::~SfxUndoContext()
43 {
44 }
45
46
~SfxUndoAction()47 SfxUndoAction::~SfxUndoAction() COVERITY_NOEXCEPT_FALSE
48 {
49 }
50
51
SfxUndoAction()52 SfxUndoAction::SfxUndoAction()
53 : m_aDateTime(DateTime::SYSTEM)
54 {
55 m_aDateTime.ConvertToUTC();
56 }
57
58
Merge(SfxUndoAction *)59 bool SfxUndoAction::Merge( SfxUndoAction * )
60 {
61 return false;
62 }
63
64
GetComment() const65 OUString SfxUndoAction::GetComment() const
66 {
67 return OUString();
68 }
69
70
GetViewShellId() const71 ViewShellId SfxUndoAction::GetViewShellId() const
72 {
73 return ViewShellId(-1);
74 }
75
GetDateTime() const76 const DateTime& SfxUndoAction::GetDateTime() const
77 {
78 return m_aDateTime;
79 }
80
GetRepeatComment(SfxRepeatTarget &) const81 OUString SfxUndoAction::GetRepeatComment(SfxRepeatTarget&) const
82 {
83 return GetComment();
84 }
85
86
Undo()87 void SfxUndoAction::Undo()
88 {
89 // These are only conceptually pure virtual
90 assert(!"pure virtual function called: SfxUndoAction::Undo()");
91 }
92
93
UndoWithContext(SfxUndoContext &)94 void SfxUndoAction::UndoWithContext( SfxUndoContext& )
95 {
96 Undo();
97 }
98
99
Redo()100 void SfxUndoAction::Redo()
101 {
102 // These are only conceptually pure virtual
103 assert(!"pure virtual function called: SfxUndoAction::Redo()");
104 }
105
106
RedoWithContext(SfxUndoContext &)107 void SfxUndoAction::RedoWithContext( SfxUndoContext& )
108 {
109 Redo();
110 }
111
112
Repeat(SfxRepeatTarget &)113 void SfxUndoAction::Repeat(SfxRepeatTarget&)
114 {
115 // These are only conceptually pure virtual
116 assert(!"pure virtual function called: SfxUndoAction::Repeat()");
117 }
118
119
CanRepeat(SfxRepeatTarget &) const120 bool SfxUndoAction::CanRepeat(SfxRepeatTarget&) const
121 {
122 return true;
123 }
124
dumpAsXml(xmlTextWriterPtr pWriter) const125 void SfxUndoAction::dumpAsXml(xmlTextWriterPtr pWriter) const
126 {
127 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SfxUndoAction"));
128 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
129 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("symbol"), BAD_CAST(typeid(*this).name()));
130 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("comment"), BAD_CAST(GetComment().toUtf8().getStr()));
131 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("viewShellId"), BAD_CAST(OString::number(static_cast<sal_Int32>(GetViewShellId())).getStr()));
132 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("dateTime"), BAD_CAST(utl::toISO8601(m_aDateTime.GetUNODateTime()).toUtf8().getStr()));
133 (void)xmlTextWriterEndElement(pWriter);
134 }
135
Remove(int idx)136 std::unique_ptr<SfxUndoAction> SfxUndoArray::Remove(int idx)
137 {
138 auto ret = std::move(maUndoActions[idx].pAction);
139 maUndoActions.erase(maUndoActions.begin() + idx);
140 return ret;
141 }
142
Remove(size_t i_pos,size_t i_count)143 void SfxUndoArray::Remove( size_t i_pos, size_t i_count )
144 {
145 maUndoActions.erase(maUndoActions.begin() + i_pos, maUndoActions.begin() + i_pos + i_count);
146 }
147
Insert(std::unique_ptr<SfxUndoAction> i_action,size_t i_pos)148 void SfxUndoArray::Insert( std::unique_ptr<SfxUndoAction> i_action, size_t i_pos )
149 {
150 maUndoActions.insert( maUndoActions.begin() + i_pos, MarkedUndoAction(std::move(i_action)) );
151 }
152
153 typedef ::std::vector< SfxUndoListener* > UndoListeners;
154
155 struct SfxUndoManager_Data
156 {
157 ::osl::Mutex aMutex;
158 std::unique_ptr<SfxUndoArray>
159 pUndoArray;
160 SfxUndoArray* pActUndoArray;
161
162 sal_Int32 mnMarks;
163 sal_Int32 mnEmptyMark;
164 bool mbUndoEnabled;
165 bool mbDoing;
166 bool mbClearUntilTopLevel;
167 bool mbEmptyActions;
168
169 UndoListeners aListeners;
170
SfxUndoManager_DataSfxUndoManager_Data171 explicit SfxUndoManager_Data( size_t i_nMaxUndoActionCount )
172 :pUndoArray( new SfxUndoArray( i_nMaxUndoActionCount ) )
173 ,pActUndoArray( nullptr )
174 ,mnMarks( 0 )
175 ,mnEmptyMark(MARK_INVALID)
176 ,mbUndoEnabled( true )
177 ,mbDoing( false )
178 ,mbClearUntilTopLevel( false )
179 ,mbEmptyActions( true )
180 {
181 pActUndoArray = pUndoArray.get();
182 }
183
184 // Copy assignment is forbidden and not implemented.
185 SfxUndoManager_Data (const SfxUndoManager_Data &) = delete;
186 SfxUndoManager_Data & operator= (const SfxUndoManager_Data &) = delete;
187 };
188
189 namespace svl::undo::impl
190 {
191 class LockGuard
192 {
193 public:
LockGuard(SfxUndoManager & i_manager)194 explicit LockGuard( SfxUndoManager& i_manager )
195 :m_manager( i_manager )
196 {
197 m_manager.ImplEnableUndo_Lock( false );
198 }
199
~LockGuard()200 ~LockGuard()
201 {
202 m_manager.ImplEnableUndo_Lock( true );
203 }
204
205 private:
206 SfxUndoManager& m_manager;
207 };
208
209 typedef void ( SfxUndoListener::*UndoListenerVoidMethod )();
210 typedef void ( SfxUndoListener::*UndoListenerStringMethod )( const OUString& );
211
212 namespace {
213
214 struct NotifyUndoListener
215 {
NotifyUndoListenersvl::undo::impl::__anon9251a32d0111::NotifyUndoListener216 explicit NotifyUndoListener( UndoListenerVoidMethod i_notificationMethod )
217 :m_notificationMethod( i_notificationMethod )
218 ,m_altNotificationMethod( nullptr )
219 ,m_sActionComment()
220 {
221 }
222
NotifyUndoListenersvl::undo::impl::__anon9251a32d0111::NotifyUndoListener223 NotifyUndoListener( UndoListenerStringMethod i_notificationMethod, const OUString& i_actionComment )
224 :m_notificationMethod( nullptr )
225 ,m_altNotificationMethod( i_notificationMethod )
226 ,m_sActionComment( i_actionComment )
227 {
228 }
229
issvl::undo::impl::__anon9251a32d0111::NotifyUndoListener230 bool is() const
231 {
232 return ( m_notificationMethod != nullptr ) || ( m_altNotificationMethod != nullptr );
233 }
234
operator ()svl::undo::impl::__anon9251a32d0111::NotifyUndoListener235 void operator()( SfxUndoListener* i_listener ) const
236 {
237 assert( is() && "NotifyUndoListener: this will crash!" );
238 if ( m_altNotificationMethod != nullptr )
239 {
240 ( i_listener->*m_altNotificationMethod )( m_sActionComment );
241 }
242 else
243 {
244 ( i_listener->*m_notificationMethod )();
245 }
246 }
247
248 private:
249 UndoListenerVoidMethod m_notificationMethod;
250 UndoListenerStringMethod m_altNotificationMethod;
251 OUString m_sActionComment;
252 };
253
254 }
255
256 class UndoManagerGuard
257 {
258 public:
UndoManagerGuard(SfxUndoManager_Data & i_managerData)259 explicit UndoManagerGuard( SfxUndoManager_Data& i_managerData )
260 :m_rManagerData( i_managerData )
261 ,m_aGuard( i_managerData.aMutex )
262 ,m_notifiers()
263 {
264 }
265
266 ~UndoManagerGuard();
267
clear()268 void clear()
269 {
270 m_aGuard.clear();
271 }
272
reset()273 void reset()
274 {
275 m_aGuard.reset();
276 }
277
cancelNotifications()278 void cancelNotifications()
279 {
280 m_notifiers.clear();
281 }
282
283 /** marks the given Undo action for deletion
284
285 The Undo action will be put into a list, whose members will be deleted from within the destructor of the
286 UndoManagerGuard. This deletion will happen without the UndoManager's mutex locked.
287 */
markForDeletion(std::unique_ptr<SfxUndoAction> i_action)288 void markForDeletion( std::unique_ptr<SfxUndoAction> i_action )
289 {
290 // remember
291 assert ( i_action );
292 m_aUndoActionsCleanup.emplace_back( std::move(i_action) );
293 }
294
295 /** schedules the given SfxUndoListener method to be called for all registered listeners.
296
297 The notification will happen after the Undo manager's mutex has been released, and after all pending
298 deletions of Undo actions are done.
299 */
scheduleNotification(UndoListenerVoidMethod i_notificationMethod)300 void scheduleNotification( UndoListenerVoidMethod i_notificationMethod )
301 {
302 m_notifiers.emplace_back( i_notificationMethod );
303 }
304
scheduleNotification(UndoListenerStringMethod i_notificationMethod,const OUString & i_actionComment)305 void scheduleNotification( UndoListenerStringMethod i_notificationMethod, const OUString& i_actionComment )
306 {
307 m_notifiers.emplace_back( i_notificationMethod, i_actionComment );
308 }
309
310 private:
311 SfxUndoManager_Data& m_rManagerData;
312 ::osl::ResettableMutexGuard m_aGuard;
313 ::std::vector< std::unique_ptr<SfxUndoAction> > m_aUndoActionsCleanup;
314 ::std::vector< NotifyUndoListener > m_notifiers;
315 };
316
~UndoManagerGuard()317 UndoManagerGuard::~UndoManagerGuard()
318 {
319 // copy members
320 UndoListeners aListenersCopy( m_rManagerData.aListeners );
321
322 // release mutex
323 m_aGuard.clear();
324
325 // delete all actions
326 m_aUndoActionsCleanup.clear();
327
328 // handle scheduled notification
329 for (auto const& notifier : m_notifiers)
330 {
331 if ( notifier.is() )
332 ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(), notifier );
333 }
334 }
335 }
336
337 using namespace ::svl::undo::impl;
338
339
SfxUndoManager(size_t nMaxUndoActionCount)340 SfxUndoManager::SfxUndoManager( size_t nMaxUndoActionCount )
341 :m_xData( new SfxUndoManager_Data( nMaxUndoActionCount ) )
342 {
343 m_xData->mbEmptyActions = !ImplIsEmptyActions();
344 }
345
346
~SfxUndoManager()347 SfxUndoManager::~SfxUndoManager()
348 {
349 }
350
351
EnableUndo(bool i_enable)352 void SfxUndoManager::EnableUndo( bool i_enable )
353 {
354 UndoManagerGuard aGuard( *m_xData );
355 ImplEnableUndo_Lock( i_enable );
356
357 }
358
359
ImplEnableUndo_Lock(bool const i_enable)360 void SfxUndoManager::ImplEnableUndo_Lock( bool const i_enable )
361 {
362 if ( m_xData->mbUndoEnabled == i_enable )
363 return;
364 m_xData->mbUndoEnabled = i_enable;
365 }
366
367
IsUndoEnabled() const368 bool SfxUndoManager::IsUndoEnabled() const
369 {
370 UndoManagerGuard aGuard( *m_xData );
371 return ImplIsUndoEnabled_Lock();
372 }
373
374
ImplIsUndoEnabled_Lock() const375 bool SfxUndoManager::ImplIsUndoEnabled_Lock() const
376 {
377 return m_xData->mbUndoEnabled;
378 }
379
380
SetMaxUndoActionCount(size_t nMaxUndoActionCount)381 void SfxUndoManager::SetMaxUndoActionCount( size_t nMaxUndoActionCount )
382 {
383 UndoManagerGuard aGuard( *m_xData );
384
385 // Remove entries from the pActUndoArray when we have to reduce
386 // the number of entries due to a lower nMaxUndoActionCount.
387 // Both redo and undo action entries will be removed until we reached the
388 // new nMaxUndoActionCount.
389
390 tools::Long nNumToDelete = m_xData->pActUndoArray->maUndoActions.size() - nMaxUndoActionCount;
391 while ( nNumToDelete > 0 )
392 {
393 size_t nPos = m_xData->pActUndoArray->maUndoActions.size();
394 if ( nPos > m_xData->pActUndoArray->nCurUndoAction )
395 {
396 aGuard.markForDeletion( m_xData->pActUndoArray->Remove( nPos-1 ) );
397 --nNumToDelete;
398 }
399
400 if ( nNumToDelete > 0 && m_xData->pActUndoArray->nCurUndoAction > 0 )
401 {
402 aGuard.markForDeletion( m_xData->pActUndoArray->Remove(0) );
403 --m_xData->pActUndoArray->nCurUndoAction;
404 --nNumToDelete;
405 }
406
407 if ( nPos == m_xData->pActUndoArray->maUndoActions.size() )
408 break; // Cannot delete more entries
409 }
410
411 m_xData->pActUndoArray->nMaxUndoActions = nMaxUndoActionCount;
412 ImplCheckEmptyActions();
413 }
414
415
ImplClearCurrentLevel_NoNotify(UndoManagerGuard & i_guard)416 void SfxUndoManager::ImplClearCurrentLevel_NoNotify( UndoManagerGuard& i_guard )
417 {
418 // clear array
419 while ( !m_xData->pActUndoArray->maUndoActions.empty() )
420 {
421 size_t deletePos = m_xData->pActUndoArray->maUndoActions.size() - 1;
422 i_guard.markForDeletion( m_xData->pActUndoArray->Remove( deletePos ) );
423 }
424
425 m_xData->pActUndoArray->nCurUndoAction = 0;
426
427 m_xData->mnMarks = 0;
428 m_xData->mnEmptyMark = MARK_INVALID;
429 ImplCheckEmptyActions();
430 }
431
432
Clear()433 void SfxUndoManager::Clear()
434 {
435 UndoManagerGuard aGuard( *m_xData );
436
437 SAL_WARN_IF( ImplIsInListAction_Lock(), "svl",
438 "SfxUndoManager::Clear: suspicious call - do you really wish to clear the current level?" );
439 ImplClearCurrentLevel_NoNotify( aGuard );
440
441 // notify listeners
442 aGuard.scheduleNotification( &SfxUndoListener::cleared );
443 }
444
445
ClearAllLevels()446 void SfxUndoManager::ClearAllLevels()
447 {
448 UndoManagerGuard aGuard( *m_xData );
449 ImplClearCurrentLevel_NoNotify( aGuard );
450
451 if ( ImplIsInListAction_Lock() )
452 {
453 m_xData->mbClearUntilTopLevel = true;
454 }
455 else
456 {
457 aGuard.scheduleNotification( &SfxUndoListener::cleared );
458 }
459 }
460
461
ImplClearRedo_NoLock(bool const i_currentLevel)462 void SfxUndoManager::ImplClearRedo_NoLock( bool const i_currentLevel )
463 {
464 UndoManagerGuard aGuard( *m_xData );
465 ImplClearRedo( aGuard, i_currentLevel );
466 }
467
468
ClearRedo()469 void SfxUndoManager::ClearRedo()
470 {
471 SAL_WARN_IF( IsInListAction(), "svl",
472 "SfxUndoManager::ClearRedo: suspicious call - do you really wish to clear the current level?" );
473 ImplClearRedo_NoLock( CurrentLevel );
474 }
475
476
Reset()477 void SfxUndoManager::Reset()
478 {
479 UndoManagerGuard aGuard( *m_xData );
480
481 // clear all locks
482 while ( !ImplIsUndoEnabled_Lock() )
483 ImplEnableUndo_Lock( true );
484
485 // cancel all list actions
486 while ( IsInListAction() )
487 ImplLeaveListAction( false, aGuard );
488
489 // clear both stacks
490 ImplClearCurrentLevel_NoNotify( aGuard );
491
492 // cancel the notifications scheduled by ImplLeaveListAction,
493 // as we want to do an own, dedicated notification
494 aGuard.cancelNotifications();
495
496 // schedule notification
497 aGuard.scheduleNotification( &SfxUndoListener::resetAll );
498 }
499
500
ImplClearUndo(UndoManagerGuard & i_guard)501 void SfxUndoManager::ImplClearUndo( UndoManagerGuard& i_guard )
502 {
503 while ( m_xData->pActUndoArray->nCurUndoAction > 0 )
504 {
505 i_guard.markForDeletion( m_xData->pActUndoArray->Remove( 0 ) );
506 --m_xData->pActUndoArray->nCurUndoAction;
507 }
508 ImplCheckEmptyActions();
509 // TODO: notifications? We don't have clearedUndo, only cleared and clearedRedo at the SfxUndoListener
510 }
511
512
ImplClearRedo(UndoManagerGuard & i_guard,bool const i_currentLevel)513 void SfxUndoManager::ImplClearRedo( UndoManagerGuard& i_guard, bool const i_currentLevel )
514 {
515 SfxUndoArray* pUndoArray = ( i_currentLevel == SfxUndoManager::CurrentLevel ) ? m_xData->pActUndoArray : m_xData->pUndoArray.get();
516
517 // clearance
518 while ( pUndoArray->maUndoActions.size() > pUndoArray->nCurUndoAction )
519 {
520 size_t deletePos = pUndoArray->maUndoActions.size() - 1;
521 i_guard.markForDeletion( pUndoArray->Remove( deletePos ) );
522 }
523
524 ImplCheckEmptyActions();
525 // notification - only if the top level's stack was cleared
526 if ( i_currentLevel == SfxUndoManager::TopLevel )
527 i_guard.scheduleNotification( &SfxUndoListener::clearedRedo );
528 }
529
530
ImplAddUndoAction_NoNotify(std::unique_ptr<SfxUndoAction> pAction,bool bTryMerge,bool bClearRedo,UndoManagerGuard & i_guard)531 bool SfxUndoManager::ImplAddUndoAction_NoNotify( std::unique_ptr<SfxUndoAction> pAction, bool bTryMerge, bool bClearRedo, UndoManagerGuard& i_guard )
532 {
533 if ( !ImplIsUndoEnabled_Lock() || ( m_xData->pActUndoArray->nMaxUndoActions == 0 ) )
534 {
535 i_guard.markForDeletion( std::move(pAction) );
536 return false;
537 }
538
539 // merge, if required
540 SfxUndoAction* pMergeWithAction = m_xData->pActUndoArray->nCurUndoAction ?
541 m_xData->pActUndoArray->maUndoActions[m_xData->pActUndoArray->nCurUndoAction-1].pAction.get() : nullptr;
542 if ( bTryMerge && pMergeWithAction )
543 {
544 bool bMerged = pMergeWithAction->Merge( pAction.get() );
545 if ( bMerged )
546 {
547 i_guard.markForDeletion( std::move(pAction) );
548 return false;
549 }
550 }
551
552 // clear redo stack, if requested
553 if ( bClearRedo && ( ImplGetRedoActionCount_Lock() > 0 ) )
554 ImplClearRedo( i_guard, SfxUndoManager::CurrentLevel );
555
556 // respect max number
557 if( m_xData->pActUndoArray == m_xData->pUndoArray.get() )
558 {
559 while(m_xData->pActUndoArray->maUndoActions.size() >= m_xData->pActUndoArray->nMaxUndoActions)
560 {
561 i_guard.markForDeletion( m_xData->pActUndoArray->Remove(0) );
562 if (m_xData->pActUndoArray->nCurUndoAction > 0)
563 {
564 --m_xData->pActUndoArray->nCurUndoAction;
565 }
566 else
567 {
568 assert(!"CurrentUndoAction going negative (!)");
569 }
570 // fdo#66071 invalidate the current empty mark when removing
571 --m_xData->mnEmptyMark;
572 }
573 }
574
575 // append new action
576 m_xData->pActUndoArray->Insert( std::move(pAction), m_xData->pActUndoArray->nCurUndoAction++ );
577 ImplCheckEmptyActions();
578 return true;
579 }
580
581
AddUndoAction(std::unique_ptr<SfxUndoAction> pAction,bool bTryMerge)582 void SfxUndoManager::AddUndoAction( std::unique_ptr<SfxUndoAction> pAction, bool bTryMerge )
583 {
584 UndoManagerGuard aGuard( *m_xData );
585
586 // add
587 auto pActionTmp = pAction.get();
588 if ( ImplAddUndoAction_NoNotify( std::move(pAction), bTryMerge, true, aGuard ) )
589 {
590 // notify listeners
591 aGuard.scheduleNotification( &SfxUndoListener::undoActionAdded, pActionTmp->GetComment() );
592 }
593 }
594
595
GetUndoActionCount(bool const i_currentLevel) const596 size_t SfxUndoManager::GetUndoActionCount( bool const i_currentLevel ) const
597 {
598 UndoManagerGuard aGuard( *m_xData );
599 const SfxUndoArray* pUndoArray = i_currentLevel ? m_xData->pActUndoArray : m_xData->pUndoArray.get();
600 return pUndoArray->nCurUndoAction;
601 }
602
603
GetUndoActionComment(size_t nNo,bool const i_currentLevel) const604 OUString SfxUndoManager::GetUndoActionComment( size_t nNo, bool const i_currentLevel ) const
605 {
606 UndoManagerGuard aGuard( *m_xData );
607
608 OUString sComment;
609 const SfxUndoArray* pUndoArray = i_currentLevel ? m_xData->pActUndoArray : m_xData->pUndoArray.get();
610 assert(nNo < pUndoArray->nCurUndoAction);
611 if( nNo < pUndoArray->nCurUndoAction )
612 sComment = pUndoArray->maUndoActions[ pUndoArray->nCurUndoAction - 1 - nNo ].pAction->GetComment();
613 return sComment;
614 }
615
616
GetUndoAction(size_t nNo) const617 SfxUndoAction* SfxUndoManager::GetUndoAction( size_t nNo ) const
618 {
619 UndoManagerGuard aGuard( *m_xData );
620
621 assert(nNo < m_xData->pActUndoArray->nCurUndoAction);
622 if( nNo >= m_xData->pActUndoArray->nCurUndoAction )
623 return nullptr;
624 return m_xData->pActUndoArray->maUndoActions[m_xData->pActUndoArray->nCurUndoAction-1-nNo].pAction.get();
625 }
626
627
628 /** clears the redo stack and removes the top undo action */
RemoveLastUndoAction()629 void SfxUndoManager::RemoveLastUndoAction()
630 {
631 UndoManagerGuard aGuard( *m_xData );
632
633 ENSURE_OR_RETURN_VOID( m_xData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::RemoveLastUndoAction(), no action to remove?!" );
634
635 m_xData->pActUndoArray->nCurUndoAction--;
636
637 // delete redo-actions and top action
638 for ( size_t nPos = m_xData->pActUndoArray->maUndoActions.size(); nPos > m_xData->pActUndoArray->nCurUndoAction; --nPos )
639 {
640 aGuard.markForDeletion( std::move(m_xData->pActUndoArray->maUndoActions[nPos-1].pAction) );
641 }
642
643 m_xData->pActUndoArray->Remove(
644 m_xData->pActUndoArray->nCurUndoAction,
645 m_xData->pActUndoArray->maUndoActions.size() - m_xData->pActUndoArray->nCurUndoAction );
646 ImplCheckEmptyActions();
647 }
648
649
IsDoing() const650 bool SfxUndoManager::IsDoing() const
651 {
652 UndoManagerGuard aGuard( *m_xData );
653 return m_xData->mbDoing;
654 }
655
656
Undo()657 bool SfxUndoManager::Undo()
658 {
659 return ImplUndo( nullptr );
660 }
661
662
UndoWithContext(SfxUndoContext & i_context)663 bool SfxUndoManager::UndoWithContext( SfxUndoContext& i_context )
664 {
665 return ImplUndo( &i_context );
666 }
667
668
ImplUndo(SfxUndoContext * i_contextOrNull)669 bool SfxUndoManager::ImplUndo( SfxUndoContext* i_contextOrNull )
670 {
671 UndoManagerGuard aGuard( *m_xData );
672 assert( !IsDoing() && "SfxUndoManager::Undo: *nested* Undo/Redo actions? How this?" );
673
674 ::comphelper::FlagGuard aDoingGuard( m_xData->mbDoing );
675 LockGuard aLockGuard( *this );
676
677 if ( ImplIsInListAction_Lock() )
678 {
679 assert(!"SfxUndoManager::Undo: not possible when within a list action!");
680 return false;
681 }
682
683 if ( m_xData->pActUndoArray->nCurUndoAction == 0 )
684 {
685 SAL_WARN("svl", "SfxUndoManager::Undo: undo stack is empty!" );
686 return false;
687 }
688
689 SfxUndoAction* pAction = m_xData->pActUndoArray->maUndoActions[ --m_xData->pActUndoArray->nCurUndoAction ].pAction.get();
690 const OUString sActionComment = pAction->GetComment();
691 try
692 {
693 // clear the guard/mutex before calling into the SfxUndoAction - this can be an extension-implemented UNO component
694 // nowadays ...
695 aGuard.clear();
696 if ( i_contextOrNull != nullptr )
697 pAction->UndoWithContext( *i_contextOrNull );
698 else
699 pAction->Undo();
700 aGuard.reset();
701 }
702 catch( ... )
703 {
704 aGuard.reset();
705
706 // in theory, somebody might have tampered with all of *m_xData while the mutex was unlocked. So, see if
707 // we still find pAction in our current Undo array
708 size_t nCurAction = 0;
709 while ( nCurAction < m_xData->pActUndoArray->maUndoActions.size() )
710 {
711 if ( m_xData->pActUndoArray->maUndoActions[ nCurAction++ ].pAction.get() == pAction )
712 {
713 // the Undo action is still there ...
714 // assume the error is a permanent failure, and clear the Undo stack
715 ImplClearUndo( aGuard );
716 throw;
717 }
718 }
719 SAL_WARN("svl", "SfxUndoManager::Undo: can't clear the Undo stack after the failure - some other party was faster ..." );
720 throw;
721 }
722
723 aGuard.scheduleNotification( &SfxUndoListener::actionUndone, sActionComment );
724
725 return true;
726 }
727
728
GetRedoActionCount(bool const i_currentLevel) const729 size_t SfxUndoManager::GetRedoActionCount( bool const i_currentLevel ) const
730 {
731 UndoManagerGuard aGuard( *m_xData );
732 return ImplGetRedoActionCount_Lock( i_currentLevel );
733 }
734
735
ImplGetRedoActionCount_Lock(bool const i_currentLevel) const736 size_t SfxUndoManager::ImplGetRedoActionCount_Lock( bool const i_currentLevel ) const
737 {
738 const SfxUndoArray* pUndoArray = i_currentLevel ? m_xData->pActUndoArray : m_xData->pUndoArray.get();
739 return pUndoArray->maUndoActions.size() - pUndoArray->nCurUndoAction;
740 }
741
742
GetRedoAction() const743 SfxUndoAction* SfxUndoManager::GetRedoAction() const
744 {
745 UndoManagerGuard aGuard( *m_xData );
746
747 const SfxUndoArray* pUndoArray = m_xData->pActUndoArray;
748 if ( (pUndoArray->nCurUndoAction) > pUndoArray->maUndoActions.size() )
749 {
750 return nullptr;
751 }
752 return pUndoArray->maUndoActions[ pUndoArray->nCurUndoAction ].pAction.get();
753 }
754
755
GetRedoActionComment(size_t nNo,bool const i_currentLevel) const756 OUString SfxUndoManager::GetRedoActionComment( size_t nNo, bool const i_currentLevel ) const
757 {
758 OUString sComment;
759 UndoManagerGuard aGuard( *m_xData );
760 const SfxUndoArray* pUndoArray = i_currentLevel ? m_xData->pActUndoArray : m_xData->pUndoArray.get();
761 if ( (pUndoArray->nCurUndoAction + nNo) < pUndoArray->maUndoActions.size() )
762 {
763 sComment = pUndoArray->maUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction->GetComment();
764 }
765 return sComment;
766 }
767
768
Redo()769 bool SfxUndoManager::Redo()
770 {
771 return ImplRedo( nullptr );
772 }
773
774
RedoWithContext(SfxUndoContext & i_context)775 bool SfxUndoManager::RedoWithContext( SfxUndoContext& i_context )
776 {
777 return ImplRedo( &i_context );
778 }
779
780
ImplRedo(SfxUndoContext * i_contextOrNull)781 bool SfxUndoManager::ImplRedo( SfxUndoContext* i_contextOrNull )
782 {
783 UndoManagerGuard aGuard( *m_xData );
784 assert( !IsDoing() && "SfxUndoManager::Redo: *nested* Undo/Redo actions? How this?" );
785
786 ::comphelper::FlagGuard aDoingGuard( m_xData->mbDoing );
787 LockGuard aLockGuard( *this );
788
789 if ( ImplIsInListAction_Lock() )
790 {
791 assert(!"SfxUndoManager::Redo: not possible when within a list action!");
792 return false;
793 }
794
795 if ( m_xData->pActUndoArray->nCurUndoAction >= m_xData->pActUndoArray->maUndoActions.size() )
796 {
797 SAL_WARN("svl", "SfxUndoManager::Redo: redo stack is empty!");
798 return false;
799 }
800
801 SfxUndoAction* pAction = m_xData->pActUndoArray->maUndoActions[ m_xData->pActUndoArray->nCurUndoAction++ ].pAction.get();
802 const OUString sActionComment = pAction->GetComment();
803 try
804 {
805 // clear the guard/mutex before calling into the SfxUndoAction - this can be an extension-implemented UNO component
806 // nowadays ...
807 aGuard.clear();
808 if ( i_contextOrNull != nullptr )
809 pAction->RedoWithContext( *i_contextOrNull );
810 else
811 pAction->Redo();
812 aGuard.reset();
813 }
814 catch( ... )
815 {
816 aGuard.reset();
817
818 // in theory, somebody might have tampered with all of *m_xData while the mutex was unlocked. So, see if
819 // we still find pAction in our current Undo array
820 size_t nCurAction = 0;
821 while ( nCurAction < m_xData->pActUndoArray->maUndoActions.size() )
822 {
823 if ( m_xData->pActUndoArray->maUndoActions[ nCurAction ].pAction.get() == pAction )
824 {
825 // the Undo action is still there ...
826 // assume the error is a permanent failure, and clear the Undo stack
827 ImplClearRedo( aGuard, SfxUndoManager::CurrentLevel );
828 throw;
829 }
830 ++nCurAction;
831 }
832 SAL_WARN("svl", "SfxUndoManager::Redo: can't clear the Undo stack after the failure - some other party was faster ..." );
833 throw;
834 }
835
836 ImplCheckEmptyActions();
837 aGuard.scheduleNotification( &SfxUndoListener::actionRedone, sActionComment );
838
839 return true;
840 }
841
842
GetRepeatActionCount() const843 size_t SfxUndoManager::GetRepeatActionCount() const
844 {
845 UndoManagerGuard aGuard( *m_xData );
846 return m_xData->pActUndoArray->maUndoActions.size();
847 }
848
849
GetRepeatActionComment(SfxRepeatTarget & rTarget) const850 OUString SfxUndoManager::GetRepeatActionComment(SfxRepeatTarget &rTarget) const
851 {
852 UndoManagerGuard aGuard( *m_xData );
853 return m_xData->pActUndoArray->maUndoActions[ m_xData->pActUndoArray->maUndoActions.size() - 1 ].pAction
854 ->GetRepeatComment(rTarget);
855 }
856
857
Repeat(SfxRepeatTarget & rTarget)858 bool SfxUndoManager::Repeat( SfxRepeatTarget &rTarget )
859 {
860 UndoManagerGuard aGuard( *m_xData );
861 if ( !m_xData->pActUndoArray->maUndoActions.empty() )
862 {
863 SfxUndoAction* pAction = m_xData->pActUndoArray->maUndoActions.back().pAction.get();
864 aGuard.clear();
865 if ( pAction->CanRepeat( rTarget ) )
866 pAction->Repeat( rTarget );
867 aGuard.reset(); // allow clearing in guard dtor
868 return true;
869 }
870
871 return false;
872 }
873
874
CanRepeat(SfxRepeatTarget & rTarget) const875 bool SfxUndoManager::CanRepeat( SfxRepeatTarget &rTarget ) const
876 {
877 UndoManagerGuard aGuard( *m_xData );
878 if ( !m_xData->pActUndoArray->maUndoActions.empty() )
879 {
880 size_t nActionNo = m_xData->pActUndoArray->maUndoActions.size() - 1;
881 return m_xData->pActUndoArray->maUndoActions[nActionNo].pAction->CanRepeat(rTarget);
882 }
883 return false;
884 }
885
886
AddUndoListener(SfxUndoListener & i_listener)887 void SfxUndoManager::AddUndoListener( SfxUndoListener& i_listener )
888 {
889 UndoManagerGuard aGuard( *m_xData );
890 m_xData->aListeners.push_back( &i_listener );
891 }
892
893
RemoveUndoListener(SfxUndoListener & i_listener)894 void SfxUndoManager::RemoveUndoListener( SfxUndoListener& i_listener )
895 {
896 UndoManagerGuard aGuard( *m_xData );
897 auto lookup = std::find(m_xData->aListeners.begin(), m_xData->aListeners.end(), &i_listener);
898 if (lookup != m_xData->aListeners.end())
899 m_xData->aListeners.erase( lookup );
900 }
901
902 /**
903 * Inserts a ListUndoAction and sets its UndoArray as current.
904 */
EnterListAction(const OUString & rComment,const OUString & rRepeatComment,sal_uInt16 nId,ViewShellId nViewShellId)905 void SfxUndoManager::EnterListAction( const OUString& rComment,
906 const OUString &rRepeatComment, sal_uInt16 nId,
907 ViewShellId nViewShellId )
908 {
909 UndoManagerGuard aGuard( *m_xData );
910
911 if( !ImplIsUndoEnabled_Lock() )
912 return;
913
914 if ( !m_xData->pUndoArray->nMaxUndoActions )
915 return;
916
917 SfxListUndoAction* pAction = new SfxListUndoAction( rComment, rRepeatComment, nId, nViewShellId, m_xData->pActUndoArray );
918 OSL_VERIFY( ImplAddUndoAction_NoNotify( std::unique_ptr<SfxUndoAction>(pAction), false, false, aGuard ) );
919 // expected to succeed: all conditions under which it could fail should have been checked already
920 m_xData->pActUndoArray = pAction;
921
922 // notification
923 aGuard.scheduleNotification( &SfxUndoListener::listActionEntered, rComment );
924 }
925
926
IsInListAction() const927 bool SfxUndoManager::IsInListAction() const
928 {
929 UndoManagerGuard aGuard( *m_xData );
930 return ImplIsInListAction_Lock();
931 }
932
933
ImplIsInListAction_Lock() const934 bool SfxUndoManager::ImplIsInListAction_Lock() const
935 {
936 return ( m_xData->pActUndoArray != m_xData->pUndoArray.get() );
937 }
938
939
GetListActionDepth() const940 size_t SfxUndoManager::GetListActionDepth() const
941 {
942 UndoManagerGuard aGuard( *m_xData );
943 size_t nDepth(0);
944
945 SfxUndoArray* pLookup( m_xData->pActUndoArray );
946 while ( pLookup != m_xData->pUndoArray.get() )
947 {
948 pLookup = pLookup->pFatherUndoArray;
949 ++nDepth;
950 }
951
952 return nDepth;
953 }
954
955
LeaveListAction()956 size_t SfxUndoManager::LeaveListAction()
957 {
958 UndoManagerGuard aGuard( *m_xData );
959 size_t nCount = ImplLeaveListAction( false, aGuard );
960
961 if ( m_xData->mbClearUntilTopLevel )
962 {
963 ImplClearCurrentLevel_NoNotify( aGuard );
964 if ( !ImplIsInListAction_Lock() )
965 {
966 m_xData->mbClearUntilTopLevel = false;
967 aGuard.scheduleNotification( &SfxUndoListener::cleared );
968 }
969 nCount = 0;
970 }
971
972 return nCount;
973 }
974
975
LeaveAndMergeListAction()976 size_t SfxUndoManager::LeaveAndMergeListAction()
977 {
978 UndoManagerGuard aGuard( *m_xData );
979 return ImplLeaveListAction( true, aGuard );
980 }
981
982
ImplLeaveListAction(const bool i_merge,UndoManagerGuard & i_guard)983 size_t SfxUndoManager::ImplLeaveListAction( const bool i_merge, UndoManagerGuard& i_guard )
984 {
985 if ( !ImplIsUndoEnabled_Lock() )
986 return 0;
987
988 if ( !m_xData->pUndoArray->nMaxUndoActions )
989 return 0;
990
991 if( !ImplIsInListAction_Lock() )
992 {
993 SAL_WARN("svl", "svl::SfxUndoManager::ImplLeaveListAction, called without calling EnterListAction()!" );
994 return 0;
995 }
996
997 assert(m_xData->pActUndoArray->pFatherUndoArray);
998
999 // the array/level which we're about to leave
1000 SfxUndoArray* pArrayToLeave = m_xData->pActUndoArray;
1001 // one step up
1002 m_xData->pActUndoArray = m_xData->pActUndoArray->pFatherUndoArray;
1003
1004 // If no undo actions were added to the list, delete the list action
1005 const size_t nListActionElements = pArrayToLeave->nCurUndoAction;
1006 if ( nListActionElements == 0 )
1007 {
1008 i_guard.markForDeletion( m_xData->pActUndoArray->Remove( --m_xData->pActUndoArray->nCurUndoAction ) );
1009 i_guard.scheduleNotification( &SfxUndoListener::listActionCancelled );
1010 return 0;
1011 }
1012
1013 // now that it is finally clear the list action is non-trivial, and does participate in the Undo stack, clear
1014 // the redo stack
1015 ImplClearRedo( i_guard, SfxUndoManager::CurrentLevel );
1016
1017 SfxUndoAction* pCurrentAction= m_xData->pActUndoArray->maUndoActions[ m_xData->pActUndoArray->nCurUndoAction-1 ].pAction.get();
1018 SfxListUndoAction* pListAction = dynamic_cast< SfxListUndoAction * >( pCurrentAction );
1019 ENSURE_OR_RETURN( pListAction, "SfxUndoManager::ImplLeaveListAction: list action expected at this position!", nListActionElements );
1020
1021 if ( i_merge )
1022 {
1023 // merge the list action with its predecessor on the same level
1024 SAL_WARN_IF( m_xData->pActUndoArray->nCurUndoAction <= 1, "svl",
1025 "SfxUndoManager::ImplLeaveListAction: cannot merge the list action if there's no other action on the same level - check this beforehand!" );
1026 if ( m_xData->pActUndoArray->nCurUndoAction > 1 )
1027 {
1028 std::unique_ptr<SfxUndoAction> pPreviousAction = m_xData->pActUndoArray->Remove( m_xData->pActUndoArray->nCurUndoAction - 2 );
1029 --m_xData->pActUndoArray->nCurUndoAction;
1030 pListAction->SetComment( pPreviousAction->GetComment() );
1031 pListAction->Insert( std::move(pPreviousAction), 0 );
1032 ++pListAction->nCurUndoAction;
1033 }
1034 }
1035
1036 // if the undo array has no comment, try to get it from its children
1037 if ( pListAction->GetComment().isEmpty() )
1038 {
1039 for( size_t n = 0; n < pListAction->maUndoActions.size(); n++ )
1040 {
1041 if (!pListAction->maUndoActions[n].pAction->GetComment().isEmpty())
1042 {
1043 pListAction->SetComment( pListAction->maUndoActions[n].pAction->GetComment() );
1044 break;
1045 }
1046 }
1047 }
1048
1049 ImplIsEmptyActions();
1050 // notify listeners
1051 i_guard.scheduleNotification( &SfxUndoListener::listActionLeft, pListAction->GetComment() );
1052
1053 // outta here
1054 return nListActionElements;
1055 }
1056
MarkTopUndoAction()1057 UndoStackMark SfxUndoManager::MarkTopUndoAction()
1058 {
1059 UndoManagerGuard aGuard( *m_xData );
1060
1061 SAL_WARN_IF( IsInListAction(), "svl",
1062 "SfxUndoManager::MarkTopUndoAction(): suspicious call!" );
1063 assert((m_xData->mnMarks + 1) < (m_xData->mnEmptyMark - 1) &&
1064 "SfxUndoManager::MarkTopUndoAction(): mark overflow!");
1065
1066 size_t const nActionPos = m_xData->pUndoArray->nCurUndoAction;
1067 if (0 == nActionPos)
1068 {
1069 --m_xData->mnEmptyMark;
1070 return m_xData->mnEmptyMark;
1071 }
1072
1073 m_xData->pUndoArray->maUndoActions[ nActionPos-1 ].aMarks.push_back(
1074 ++m_xData->mnMarks );
1075 return m_xData->mnMarks;
1076 }
1077
RemoveMark(UndoStackMark const i_mark)1078 void SfxUndoManager::RemoveMark( UndoStackMark const i_mark )
1079 {
1080 UndoManagerGuard aGuard( *m_xData );
1081
1082 if ((m_xData->mnEmptyMark < i_mark) || (MARK_INVALID == i_mark))
1083 {
1084 return; // nothing to remove
1085 }
1086 else if (i_mark == m_xData->mnEmptyMark)
1087 {
1088 --m_xData->mnEmptyMark; // never returned from MarkTop => invalid
1089 return;
1090 }
1091
1092 for ( size_t i=0; i<m_xData->pUndoArray->maUndoActions.size(); ++i )
1093 {
1094 MarkedUndoAction& rAction = m_xData->pUndoArray->maUndoActions[i];
1095 auto markPos = std::find(rAction.aMarks.begin(), rAction.aMarks.end(), i_mark);
1096 if (markPos != rAction.aMarks.end())
1097 {
1098 rAction.aMarks.erase( markPos );
1099 return;
1100 }
1101 }
1102 SAL_WARN("svl", "SfxUndoManager::RemoveMark: mark not found!");
1103 // TODO: this might be too offensive. There are situations where we implicitly remove marks
1104 // without our clients, in particular the client which created the mark, having a chance to know
1105 // about this.
1106 }
1107
HasTopUndoActionMark(UndoStackMark const i_mark)1108 bool SfxUndoManager::HasTopUndoActionMark( UndoStackMark const i_mark )
1109 {
1110 UndoManagerGuard aGuard( *m_xData );
1111
1112 size_t nActionPos = m_xData->pUndoArray->nCurUndoAction;
1113 if ( nActionPos == 0 )
1114 {
1115 return (i_mark == m_xData->mnEmptyMark);
1116 }
1117
1118 const MarkedUndoAction& rAction =
1119 m_xData->pUndoArray->maUndoActions[ nActionPos-1 ];
1120
1121 return std::find(rAction.aMarks.begin(), rAction.aMarks.end(), i_mark) != rAction.aMarks.end();
1122 }
1123
1124
RemoveOldestUndoAction()1125 void SfxUndoManager::RemoveOldestUndoAction()
1126 {
1127 UndoManagerGuard aGuard( *m_xData );
1128
1129 if ( IsInListAction() && ( m_xData->pUndoArray->nCurUndoAction == 1 ) )
1130 {
1131 assert(!"SfxUndoManager::RemoveOldestUndoActions: cannot remove a not-yet-closed list action!");
1132 return;
1133 }
1134
1135 aGuard.markForDeletion( m_xData->pUndoArray->Remove( 0 ) );
1136 --m_xData->pUndoArray->nCurUndoAction;
1137 ImplCheckEmptyActions();
1138 }
1139
dumpAsXml(xmlTextWriterPtr pWriter) const1140 void SfxUndoManager::dumpAsXml(xmlTextWriterPtr pWriter) const
1141 {
1142 UndoManagerGuard aGuard(*m_xData);
1143
1144 bool bOwns = false;
1145 if (!pWriter)
1146 {
1147 pWriter = xmlNewTextWriterFilename("undo.xml", 0);
1148 xmlTextWriterSetIndent(pWriter,1);
1149 (void)xmlTextWriterSetIndentString(pWriter, BAD_CAST(" "));
1150 (void)xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr);
1151 bOwns = true;
1152 }
1153
1154 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SfxUndoManager"));
1155 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nUndoActionCount"), BAD_CAST(OString::number(GetUndoActionCount()).getStr()));
1156 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nRedoActionCount"), BAD_CAST(OString::number(GetRedoActionCount()).getStr()));
1157
1158 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("undoActions"));
1159 for (size_t i = 0; i < GetUndoActionCount(); ++i)
1160 {
1161 const SfxUndoArray* pUndoArray = m_xData->pActUndoArray;
1162 pUndoArray->maUndoActions[pUndoArray->nCurUndoAction - 1 - i].pAction->dumpAsXml(pWriter);
1163 }
1164 (void)xmlTextWriterEndElement(pWriter);
1165
1166 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("redoActions"));
1167 for (size_t i = 0; i < GetRedoActionCount(); ++i)
1168 {
1169 const SfxUndoArray* pUndoArray = m_xData->pActUndoArray;
1170 pUndoArray->maUndoActions[pUndoArray->nCurUndoAction + i].pAction->dumpAsXml(pWriter);
1171 }
1172 (void)xmlTextWriterEndElement(pWriter);
1173
1174 (void)xmlTextWriterEndElement(pWriter);
1175 if (bOwns)
1176 {
1177 (void)xmlTextWriterEndDocument(pWriter);
1178 xmlFreeTextWriter(pWriter);
1179 }
1180 }
1181
1182 /// Returns a JSON representation of pAction.
lcl_ActionToJson(size_t nIndex,SfxUndoAction const * pAction)1183 static boost::property_tree::ptree lcl_ActionToJson(size_t nIndex, SfxUndoAction const * pAction)
1184 {
1185 boost::property_tree::ptree aRet;
1186 aRet.put("index", nIndex);
1187 aRet.put("comment", pAction->GetComment().toUtf8().getStr());
1188 aRet.put("viewId", static_cast<sal_Int32>(pAction->GetViewShellId()));
1189 aRet.put("dateTime", utl::toISO8601(pAction->GetDateTime().GetUNODateTime()).toUtf8().getStr());
1190 return aRet;
1191 }
1192
GetUndoActionsInfo() const1193 OUString SfxUndoManager::GetUndoActionsInfo() const
1194 {
1195 boost::property_tree::ptree aActions;
1196 const SfxUndoArray* pUndoArray = m_xData->pActUndoArray;
1197 for (size_t i = 0; i < GetUndoActionCount(); ++i)
1198 {
1199 boost::property_tree::ptree aAction = lcl_ActionToJson(i, pUndoArray->maUndoActions[pUndoArray->nCurUndoAction - 1 - i].pAction.get());
1200 aActions.push_back(std::make_pair("", aAction));
1201 }
1202
1203 boost::property_tree::ptree aTree;
1204 aTree.add_child("actions", aActions);
1205 std::stringstream aStream;
1206 boost::property_tree::write_json(aStream, aTree);
1207 return OUString::fromUtf8(aStream.str().c_str());
1208 }
1209
GetRedoActionsInfo() const1210 OUString SfxUndoManager::GetRedoActionsInfo() const
1211 {
1212 boost::property_tree::ptree aActions;
1213 const SfxUndoArray* pUndoArray = m_xData->pActUndoArray;
1214 size_t nCount = GetRedoActionCount();
1215 for (size_t i = 0; i < nCount; ++i)
1216 {
1217 size_t nIndex = nCount - i - 1;
1218 boost::property_tree::ptree aAction = lcl_ActionToJson(nIndex, pUndoArray->maUndoActions[pUndoArray->nCurUndoAction + nIndex].pAction.get());
1219 aActions.push_back(std::make_pair("", aAction));
1220 }
1221
1222 boost::property_tree::ptree aTree;
1223 aTree.add_child("actions", aActions);
1224 std::stringstream aStream;
1225 boost::property_tree::write_json(aStream, aTree);
1226 return OUString::fromUtf8(aStream.str().c_str());
1227 }
1228
IsEmptyActions() const1229 bool SfxUndoManager::IsEmptyActions() const
1230 {
1231 UndoManagerGuard aGuard(*m_xData);
1232
1233 return ImplIsEmptyActions();
1234 }
1235
ImplIsEmptyActions() const1236 inline bool SfxUndoManager::ImplIsEmptyActions() const
1237 {
1238 return m_xData->pUndoArray->nCurUndoAction || m_xData->pUndoArray->maUndoActions.size() - m_xData->pUndoArray->nCurUndoAction;
1239 }
1240
ImplCheckEmptyActions()1241 void SfxUndoManager::ImplCheckEmptyActions()
1242 {
1243 bool bEmptyActions = ImplIsEmptyActions();
1244 if (m_xData->mbEmptyActions != bEmptyActions)
1245 {
1246 m_xData->mbEmptyActions = bEmptyActions;
1247 EmptyActionsChanged();
1248 }
1249 }
1250
EmptyActionsChanged()1251 void SfxUndoManager::EmptyActionsChanged()
1252 {
1253
1254 }
1255
1256 struct SfxListUndoAction::Impl
1257 {
1258 sal_uInt16 mnId;
1259 ViewShellId mnViewShellId;
1260
1261 OUString maComment;
1262 OUString maRepeatComment;
1263
ImplSfxListUndoAction::Impl1264 Impl( sal_uInt16 nId, ViewShellId nViewShellId, const OUString& rComment, const OUString& rRepeatComment ) :
1265 mnId(nId), mnViewShellId(nViewShellId), maComment(rComment), maRepeatComment(rRepeatComment) {}
1266 };
1267
GetId() const1268 sal_uInt16 SfxListUndoAction::GetId() const
1269 {
1270 return mpImpl->mnId;
1271 }
1272
GetComment() const1273 OUString SfxListUndoAction::GetComment() const
1274 {
1275 return mpImpl->maComment;
1276 }
1277
GetViewShellId() const1278 ViewShellId SfxListUndoAction::GetViewShellId() const
1279 {
1280 return mpImpl->mnViewShellId;
1281 }
1282
SetComment(const OUString & rComment)1283 void SfxListUndoAction::SetComment(const OUString& rComment)
1284 {
1285 mpImpl->maComment = rComment;
1286 }
1287
GetRepeatComment(SfxRepeatTarget &) const1288 OUString SfxListUndoAction::GetRepeatComment(SfxRepeatTarget &) const
1289 {
1290 return mpImpl->maRepeatComment;
1291 }
1292
SfxListUndoAction(const OUString & rComment,const OUString & rRepeatComment,sal_uInt16 nId,ViewShellId nViewShellId,SfxUndoArray * pFather)1293 SfxListUndoAction::SfxListUndoAction(
1294 const OUString &rComment,
1295 const OUString &rRepeatComment,
1296 sal_uInt16 nId,
1297 ViewShellId nViewShellId,
1298 SfxUndoArray *pFather ) :
1299 mpImpl(new Impl(nId, nViewShellId, rComment, rRepeatComment))
1300 {
1301 pFatherUndoArray = pFather;
1302 nMaxUndoActions = USHRT_MAX;
1303 }
1304
~SfxListUndoAction()1305 SfxListUndoAction::~SfxListUndoAction()
1306 {
1307 }
1308
Undo()1309 void SfxListUndoAction::Undo()
1310 {
1311 for(size_t i=nCurUndoAction;i>0;)
1312 maUndoActions[--i].pAction->Undo();
1313 nCurUndoAction=0;
1314 }
1315
1316
UndoWithContext(SfxUndoContext & i_context)1317 void SfxListUndoAction::UndoWithContext( SfxUndoContext& i_context )
1318 {
1319 for(size_t i=nCurUndoAction;i>0;)
1320 maUndoActions[--i].pAction->UndoWithContext( i_context );
1321 nCurUndoAction=0;
1322 }
1323
1324
Redo()1325 void SfxListUndoAction::Redo()
1326 {
1327 for(size_t i=nCurUndoAction;i<maUndoActions.size();i++)
1328 maUndoActions[i].pAction->Redo();
1329 nCurUndoAction = maUndoActions.size();
1330 }
1331
1332
RedoWithContext(SfxUndoContext & i_context)1333 void SfxListUndoAction::RedoWithContext( SfxUndoContext& i_context )
1334 {
1335 for(size_t i=nCurUndoAction;i<maUndoActions.size();i++)
1336 maUndoActions[i].pAction->RedoWithContext( i_context );
1337 nCurUndoAction = maUndoActions.size();
1338 }
1339
1340
Repeat(SfxRepeatTarget & rTarget)1341 void SfxListUndoAction::Repeat(SfxRepeatTarget&rTarget)
1342 {
1343 for(size_t i=0;i<nCurUndoAction;i++)
1344 maUndoActions[i].pAction->Repeat(rTarget);
1345 }
1346
1347
CanRepeat(SfxRepeatTarget & r) const1348 bool SfxListUndoAction::CanRepeat(SfxRepeatTarget&r) const
1349 {
1350 for(size_t i=0;i<nCurUndoAction;i++)
1351 {
1352 if(!maUndoActions[i].pAction->CanRepeat(r))
1353 return false;
1354 }
1355 return true;
1356 }
1357
1358
Merge(SfxUndoAction * pNextAction)1359 bool SfxListUndoAction::Merge( SfxUndoAction *pNextAction )
1360 {
1361 return !maUndoActions.empty() && maUndoActions[maUndoActions.size()-1].pAction->Merge( pNextAction );
1362 }
1363
dumpAsXml(xmlTextWriterPtr pWriter) const1364 void SfxListUndoAction::dumpAsXml(xmlTextWriterPtr pWriter) const
1365 {
1366 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SfxListUndoAction"));
1367 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("size"), BAD_CAST(OString::number(maUndoActions.size()).getStr()));
1368 SfxUndoAction::dumpAsXml(pWriter);
1369
1370 for (size_t i = 0; i < maUndoActions.size(); ++i)
1371 maUndoActions[i].pAction->dumpAsXml(pWriter);
1372
1373 (void)xmlTextWriterEndElement(pWriter);
1374 }
1375
~SfxUndoArray()1376 SfxUndoArray::~SfxUndoArray()
1377 {
1378 }
1379
1380 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1381