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 #ifndef INCLUDED_SVL_UNDO_HXX
20 #define INCLUDED_SVL_UNDO_HXX
21 
22 #include <svl/svldllapi.h>
23 #include <rtl/ustring.hxx>
24 #include <tools/datetime.hxx>
25 #include <o3tl/strong_int.hxx>
26 
27 #include <memory>
28 #include <vector>
29 
30 typedef o3tl::strong_int<sal_Int32, struct ViewShellIdTag> ViewShellId;
31 typedef o3tl::strong_int<int, struct ViewShellDocIdTag> ViewShellDocId;
32 
33 typedef struct _xmlTextWriter* xmlTextWriterPtr;
34 
35 class SVL_DLLPUBLIC SfxRepeatTarget
36 {
37 public:
38     virtual             ~SfxRepeatTarget() = 0;
39 };
40 
41 
42 class SVL_DLLPUBLIC SfxUndoContext
43 {
44 public:
45     virtual             ~SfxUndoContext() = 0;
46 };
47 
48 
49 class SVL_DLLPUBLIC SfxUndoAction
50 {
51 public:
52                             SfxUndoAction();
53     virtual                 ~SfxUndoAction() COVERITY_NOEXCEPT_FALSE;
54 
55     virtual void            Undo();
56     virtual void            UndoWithContext( SfxUndoContext& i_context );
57     virtual void            Redo();
58     virtual void            RedoWithContext( SfxUndoContext& i_context );
59     virtual void            Repeat(SfxRepeatTarget&);
60     virtual bool            CanRepeat(SfxRepeatTarget&) const;
61 
62     virtual bool            Merge( SfxUndoAction *pNextAction );
63 
64     virtual OUString    GetComment() const;
65     virtual OUString    GetRepeatComment(SfxRepeatTarget&) const;
66     /// ID of the view shell that created this undo action.
67     virtual ViewShellId GetViewShellId() const;
68     /// Timestamp when this undo item was created.
69     const DateTime& GetDateTime() const;
70     virtual void dumpAsXml(xmlTextWriterPtr pWriter) const;
71 
72 private:
73     SfxUndoAction( const SfxUndoAction& ) = delete;
74     SfxUndoAction& operator=( const SfxUndoAction& ) = delete;
75 
76     DateTime m_aDateTime;
77 };
78 
79 
80 /// is a mark on the Undo stack
81 typedef sal_Int32 UndoStackMark;
82 #define MARK_INVALID    ::std::numeric_limits< UndoStackMark >::max()
83 
84 struct MarkedUndoAction
85 {
86     std::unique_ptr<SfxUndoAction>  pAction;
87     ::std::vector< UndoStackMark >  aMarks;
88 
MarkedUndoActionMarkedUndoAction89     MarkedUndoAction(std::unique_ptr<SfxUndoAction> p) : pAction(std::move(p)) {}
90 };
91 
92 /** do not make use of these implementation details, unless you
93     really really have to! */
94 struct SVL_DLLPUBLIC SfxUndoArray
95 {
96     std::vector<MarkedUndoAction> maUndoActions;
97     size_t                  nMaxUndoActions;
98     size_t                  nCurUndoAction;
99     SfxUndoArray            *pFatherUndoArray;
100 
SfxUndoArraySfxUndoArray101     SfxUndoArray(size_t nMax=0) :
102         nMaxUndoActions(nMax), nCurUndoAction(0), pFatherUndoArray(nullptr) {}
103     virtual ~SfxUndoArray();
104 
105     SfxUndoArray& operator=( SfxUndoArray const & ) = delete; // MSVC2017 workaround
106     SfxUndoArray( SfxUndoArray const & ) = delete; // MSVC2017 workaround
107 
GetUndoActionSfxUndoArray108     SfxUndoAction* GetUndoAction(size_t idx) { return maUndoActions[idx].pAction.get(); }
109     std::unique_ptr<SfxUndoAction> Remove(int idx);
110     void Remove( size_t i_pos, size_t i_count );
111     void Insert( std::unique_ptr<SfxUndoAction> i_action, size_t i_pos );
112 };
113 
114 
115 /** do not make use of these implementation details, unless you
116     really really have to! */
117 class SVL_DLLPUBLIC SfxListUndoAction final : public SfxUndoAction, public SfxUndoArray
118 
119 /*  [Explanation]
120 
121     UndoAction to composite multiple Undos in one UndoAction.
122     These actions are used by SfxUndomanager. With < SfxUndoManager::EnterListAction >
123     you can go one composite level down and with < SfxUndoManager::LeaveListAction > up again.
124     Redo and Undo work element wise on SfxListUndoActions.
125 */
126 {
127     struct Impl;
128     std::unique_ptr<Impl> mpImpl;
129 
130 public:
131 
132     SfxListUndoAction(
133         const OUString &rComment, const OUString& rRepeatComment, sal_uInt16 nId, ViewShellId nViewShellId, SfxUndoArray *pFather );
134     virtual ~SfxListUndoAction() override;
135 
136     virtual void            Undo() override;
137     virtual void            UndoWithContext( SfxUndoContext& i_context ) override;
138     virtual void            Redo() override;
139     virtual void            RedoWithContext( SfxUndoContext& i_context ) override;
140     virtual void            Repeat(SfxRepeatTarget&) override;
141     virtual bool            CanRepeat(SfxRepeatTarget&) const override;
142 
143     virtual bool            Merge( SfxUndoAction *pNextAction ) override;
144 
145     virtual OUString        GetComment() const override;
146     /// See SfxUndoAction::GetViewShellId().
147     ViewShellId GetViewShellId() const override;
148     virtual OUString        GetRepeatComment(SfxRepeatTarget&) const override;
149     sal_uInt16              GetId() const;
150 
151     void SetComment(const OUString& rComment);
152     void dumpAsXml(xmlTextWriterPtr pWriter) const override;
153 };
154 
155 
156 /**  is a callback interface for notifications about state changes of an SfxUndoManager
157 */
158 class SAL_NO_VTABLE SfxUndoListener
159 {
160 public:
161     virtual void actionUndone( const OUString& i_actionComment ) = 0;
162     virtual void actionRedone( const OUString& i_actionComment ) = 0;
163     virtual void undoActionAdded( const OUString& i_actionComment ) = 0;
164     virtual void cleared() = 0;
165     virtual void clearedRedo() = 0;
166     virtual void resetAll() = 0;
167     virtual void listActionEntered( const OUString& i_comment ) = 0;
168     virtual void listActionLeft( const OUString& i_comment ) = 0;
169     virtual void listActionCancelled() = 0;
170 
171 protected:
~SfxUndoListener()172     ~SfxUndoListener() {}
173 };
174 
175 
176 namespace svl::undo::impl
177 {
178     class UndoManagerGuard;
179     class LockGuard;
180 }
181 
182 struct SfxUndoManager_Data;
183 class SVL_DLLPUBLIC SfxUndoManager
184 {
185     std::unique_ptr< SfxUndoManager_Data >
186                             m_xData;
187 public:
188     static bool const CurrentLevel = true;
189     static bool const TopLevel = false;
190 
191                             SfxUndoManager( size_t nMaxUndoActionCount = 20 );
192     virtual                 ~SfxUndoManager();
193 
194     void                    SetMaxUndoActionCount( size_t nMaxUndoActionCount );
195     virtual void            AddUndoAction( std::unique_ptr<SfxUndoAction> pAction, bool bTryMerg=false );
196     virtual size_t          GetUndoActionCount( bool const i_currentLevel = CurrentLevel ) const;
197     OUString                GetUndoActionComment( size_t nNo=0, bool const i_currentLevel = CurrentLevel ) const;
198     SfxUndoAction*          GetUndoAction( size_t nNo=0 ) const;
199     /// Get info about all undo actions (comment, view shell id, etc.)
200     OUString                GetUndoActionsInfo() const;
201     virtual size_t          GetRedoActionCount( bool const i_currentLevel = CurrentLevel ) const;
202     OUString                GetRedoActionComment( size_t nNo=0, bool const i_currentLevel = CurrentLevel ) const;
203     SfxUndoAction*          GetRedoAction() const;
204     /// Get info about all redo actions (comment, view shell id, etc.)
205     OUString                GetRedoActionsInfo() const;
206     virtual bool            Undo();
207     virtual bool            Redo();
208     /** Clears both the Redo and the Undo stack.
209         Will assert and bail out when called while within a list action (<member>IsInListAction</member>).
210     */
211     virtual void            Clear();
212     /** Clears the Redo stack.
213         Will assert and bail out when called while within a list action (<member>IsInListAction</member>).
214     */
215     virtual void            ClearRedo();
216     /** leaves any possible open list action (<member>IsInListAction</member>), and clears both the Undo and the
217         Redo stack.
218 
219         Effectively, calling this method is equivalent to <code>while ( IsInListAction() ) LeaveListAction();</code>,
220         followed by <code>Clear()</code>. The only difference to this calling sequence is that Reset is an
221         atomic operation, also resulting in only one notification.
222     */
223     void                    Reset();
224     /** determines whether an Undo or Redo is currently running
225     */
226     bool                    IsDoing() const;
227     size_t                  GetRepeatActionCount() const;
228     OUString                GetRepeatActionComment( SfxRepeatTarget &rTarget) const;
229     bool                    Repeat( SfxRepeatTarget &rTarget );
230     bool                    CanRepeat( SfxRepeatTarget &rTarget ) const;
231     virtual void            EnterListAction(const OUString &rComment, const OUString& rRepeatComment, sal_uInt16 nId, ViewShellId nViewShellId);
232     /** Leaves the list action entered with EnterListAction
233         @return the number of the sub actions in the list which has just been left. Note that in case no such
234             actions exist, the list action does not contribute to the Undo stack, but is silently removed.
235     */
236     size_t                  LeaveListAction();
237 
238     /** Leaves the list action entered with EnterListAction, and forcefully merges the previous
239         action on the stack into the newly created list action.
240 
241         Say you have an Undo action A on the stack, then call EnterListAction, followed by one or more calls to
242         AddUndoAction, followed by a call to LeaveAndMergeListAction. In opposite to LeaveListAction, your Undo
243         stack will now still contain one undo action: the newly created list action, whose first child is the
244         original A, whose other children are those you added via AddUndoAction, and whose comment is the same as
245         the comment of A.
246 
247         Effectively, this means that all actions added between EnterListAction and LeaveAndMergeListAction are
248         hidden from the user.
249 
250         @return the number of the sub actions in the list which has just been left. Note that in case no such
251             actions exist, the list action does not contribute to the Undo stack, but is silently removed.
252     */
253     size_t                  LeaveAndMergeListAction();
254     /// determines whether we're within a ListAction context, i.e. a LeaveListAction/LeaveAndMergeListAction call is pending
255     bool                    IsInListAction() const;
256     /// Determines how many nested list actions are currently open
257     size_t                  GetListActionDepth() const;
258     /** Clears the redo stack and removes the top undo action */
259     void                    RemoveLastUndoAction();
260     /** enables (true) or disables (false) recording of undo actions
261 
262         If undo actions are added while undo is disabled, they are deleted.
263         Disabling undo does not clear the current undo buffer!
264 
265         Multiple calls to <code>EnableUndo</code> are not cumulative. That is, calling <code>EnableUndo( false )</code>
266         twice, and then calling <code>EnableUndo( true )</code> means that Undo is enable afterwards.
267     */
268     void                    EnableUndo( bool bEnable );
269     /// returns true if undo is currently enabled.
270     /// This returns false if undo was disabled using EnableUndo( false ) and
271     /// also during the runtime of the Undo() and Redo() methods.
272     bool                    IsUndoEnabled() const;
273     /// Adds a new listener to be notified about changes in the UndoManager's state
274     void                    AddUndoListener( SfxUndoListener& i_listener );
275     void                    RemoveUndoListener( SfxUndoListener& i_listener );
276     bool                    IsEmptyActions() const;
277 
278 
279     /** marks the current top-level element of the Undo stack, and returns a unique ID for it
280     */
281     UndoStackMark   MarkTopUndoAction();
282 
283     /** removes a mark given by its ID.
284         After the call, the mark ID is invalid.
285     */
286     void            RemoveMark( UndoStackMark const i_mark );
287 
288     /** determines whether the top action on the Undo stack has a given mark
289     */
290     bool            HasTopUndoActionMark( UndoStackMark const i_mark );
291 
292     /** removes the oldest Undo actions from the stack
293     */
294     void            RemoveOldestUndoAction();
295 
296     void dumpAsXml(xmlTextWriterPtr pWriter) const;
297 
298 protected:
299     bool    UndoWithContext( SfxUndoContext& i_context );
300     bool    RedoWithContext( SfxUndoContext& i_context );
301 
302     void    ImplClearRedo_NoLock( bool const i_currentLevel );
303 
304     /** clears all undo actions on the current level, plus all undo actions on superordinate levels,
305         as soon as those levels are reached.
306 
307         If no list action is active currently, i.e. we're on the top level already, this method is equivalent to
308         ->Clear.
309 
310         Otherwise, the Undo actions on the current level are removed. Upon leaving the current list action, all
311         undo actions on the then-current level are removed, too. This is continued until the top level is reached.
312     */
313     void    ClearAllLevels();
314     virtual void EmptyActionsChanged();
315 
316 private:
317     size_t  ImplLeaveListAction( const bool i_merge, ::svl::undo::impl::UndoManagerGuard& i_guard );
318     bool    ImplAddUndoAction_NoNotify( std::unique_ptr<SfxUndoAction> pAction, bool bTryMerge, bool bClearRedo, ::svl::undo::impl::UndoManagerGuard& i_guard );
319     void    ImplClearRedo( ::svl::undo::impl::UndoManagerGuard& i_guard, bool const i_currentLevel );
320     void    ImplClearUndo( ::svl::undo::impl::UndoManagerGuard& i_guard );
321     void    ImplClearCurrentLevel_NoNotify( ::svl::undo::impl::UndoManagerGuard& i_guard );
322     size_t  ImplGetRedoActionCount_Lock( bool const i_currentLevel = CurrentLevel ) const;
323     bool    ImplIsUndoEnabled_Lock() const;
324     bool    ImplIsInListAction_Lock() const;
325     void    ImplEnableUndo_Lock( bool const i_enable );
326 
327     bool    ImplUndo( SfxUndoContext* i_contextOrNull );
328     bool    ImplRedo( SfxUndoContext* i_contextOrNull );
329     void    ImplCheckEmptyActions();
330     inline  bool    ImplIsEmptyActions() const;
331 
332     friend class ::svl::undo::impl::LockGuard;
333 };
334 
335 #endif
336 
337 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
338