1 /********************************************************************************
2 *                                                                               *
3 *                  U n d o / R e d o - a b l e   C o m m a n d                  *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 2000,2020 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify          *
9 * it under the terms of the GNU Lesser General Public License as published by   *
10 * the Free Software Foundation; either version 3 of the License, or             *
11 * (at your option) any later version.                                           *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
16 * GNU Lesser General Public License for more details.                           *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public License      *
19 * along with this program.  If not, see <http://www.gnu.org/licenses/>          *
20 ********************************************************************************/
21 #ifndef FXUNDOLIST_H
22 #define FXUNDOLIST_H
23 
24 #ifndef FXOBJECT_H
25 #include "FXObject.h"
26 #endif
27 
28 namespace FX {
29 
30 
31 class FXUndoList;
32 class FXCommandGroup;
33 
34 
35 /**
36 * Base class for undoable commands.  Each undo records all the
37 * information necessary to undo as well as redo a given operation.
38 * Since commands are derived from FXObject, subclassed commands can
39 * both send and receive messages (like ID_GETINTVALUE, for example).
40 */
41 class FXAPI FXCommand : public FXObject {
42   FXDECLARE_ABSTRACT(FXCommand)
43   friend class FXUndoList;
44   friend class FXCommandGroup;
45 private:
46   FXCommand *next;
47 private:
48   FXCommand(const FXCommand&);
49   FXCommand &operator=(const FXCommand&);
50 protected:
FXCommand()51   FXCommand():next(NULL){}
52 public:
53 
54   /**
55   * Undo this command; this should save the
56   * information for a subsequent redo.
57   */
58   virtual void undo() = 0;
59 
60   /**
61   * Redo this command; this should save the
62   * information for a subsequent undo.
63   */
64   virtual void redo() = 0;
65 
66   /**
67   * Return the size of the information in the undo record.
68   * The undo list may be trimmed to limit memory usage to
69   * a certain limit.  The value returned should include
70   * the size of the command record itself as well as any
71   * data linked from it.
72   */
73   virtual FXuint size() const;
74 
75   /**
76   * Name of the undo command to be shown on a button;
77   * for example, "Undo Delete".
78   */
79   virtual FXString undoName() const;
80 
81   /**
82   * Name of the redo command to be shown on a button;
83   * for example, "Redo Delete".
84   */
85   virtual FXString redoName() const;
86 
87   /**
88   * Return true if this command can be merged with previous undo
89   * commands.  This is useful to combine e.g. multiple consecutive
90   * single-character text changes into a single block change.
91   * The default implementation returns false.
92   */
93   virtual FXbool canMerge() const;
94 
95   /**
96   * Called by the undo system to try and merge the new incoming command
97   * with this command; should return true if merging was possible.
98   * The default implementation returns false.
99   */
100   virtual FXbool mergeWith(FXCommand* command);
101 
102   /// Delete undo command
~FXCommand()103   virtual ~FXCommand(){}
104   };
105 
106 
107 
108 /**
109 * Group of undoable commands.  A group may comprise multiple
110 * individual actions which together undo (or redo) a larger
111 * operation.  Even larger operations may be built by nesting
112 * multiple undo groups.
113 */
114 class FXAPI FXCommandGroup : public FXCommand {
115   FXDECLARE(FXCommandGroup)
116   friend class FXUndoList;
117 private:
118   FXCommand      *undolist;
119   FXCommand      *redolist;
120   FXCommandGroup *group;
121 private:
122   FXCommandGroup(const FXCommandGroup&);
123   FXCommandGroup &operator=(const FXCommandGroup&);
124 public:
125 
126   /// Construct initially empty undo command group
FXCommandGroup()127   FXCommandGroup():undolist(NULL),redolist(NULL),group(NULL){}
128 
129   /// Return true if empty
empty()130   FXbool empty(){ return !undolist; }
131 
132   /// Undo whole command group
133   virtual void undo();
134 
135   /// Redo whole command group
136   virtual void redo();
137 
138   /// Return the size of the command group
139   virtual FXuint size() const;
140 
141   /// Delete undo command and sub-commands
142   virtual ~FXCommandGroup();
143   };
144 
145 
146 
147 /**
148 * The Undo List class manages a list of undoable commands.
149 */
150 class FXAPI FXUndoList : public FXCommandGroup {
151   FXDECLARE(FXUndoList)
152 private:
153   FXint      undocount;     // Number of undo records
154   FXint      redocount;     // Number of redo records
155   FXint      marker;        // Marker value
156   FXuint     space;         // Space taken up by all the undo records
157   FXbool     working;       // Currently busy with undo or redo
158 private:
159   FXUndoList(const FXUndoList&);
160   FXUndoList &operator=(const FXUndoList&);
161 public:
162   long onCmdUndo(FXObject*,FXSelector,void*);
163   long onUpdUndo(FXObject*,FXSelector,void*);
164   long onCmdRedo(FXObject*,FXSelector,void*);
165   long onUpdRedo(FXObject*,FXSelector,void*);
166   long onCmdClear(FXObject*,FXSelector,void*);
167   long onUpdClear(FXObject*,FXSelector,void*);
168   long onCmdRevert(FXObject*,FXSelector,void*);
169   long onUpdRevert(FXObject*,FXSelector,void*);
170   long onCmdUndoAll(FXObject*,FXSelector,void*);
171   long onCmdRedoAll(FXObject*,FXSelector,void*);
172   long onUpdUndoCount(FXObject*,FXSelector,void*);
173   long onUpdRedoCount(FXObject*,FXSelector,void*);
174 public:
175   enum{
176     ID_CLEAR=FXWindow::ID_LAST,
177     ID_REVERT,
178     ID_UNDO,
179     ID_REDO,
180     ID_UNDO_ALL,
181     ID_REDO_ALL,
182     ID_UNDO_COUNT,
183     ID_REDO_COUNT,
184     ID_LAST
185     };
186 public:
187 
188   /**
189   * Make new empty undo list, initially unmarked.
190   */
191   FXUndoList();
192 
193   /**
194   * Cut the redo list.
195   * This is automatically invoked when a new undo command is added.
196   */
197   void cut();
198 
199   /**
200   * Add new command, executing it if desired. The new command will be merged
201   * with the previous command if merge is true and we're not at a marked position
202   * and the commands are mergeable.  Otherwise the new command will be appended
203   * after the last undo command in the currently active undo group.
204   * If the new command is successfully merged, it will be deleted.  Furthermore,
205   * all redo commands will be deleted since it is no longer possible to redo
206   * from this point.
207   */
208   void add(FXCommand* command,FXbool doit=false,FXbool merge=true);
209 
210   /**
211   * Begin undo command sub-group. This begins a new group of commands that
212   * are treated as a single command.  Must eventually be followed by a
213   * matching end() after recording the sub-commands.  The new sub-group
214   * will be appended to its parent group's undo list when end() is called.
215   */
216   void begin(FXCommandGroup *command);
217 
218   /**
219   * End undo command sub-group.  If the sub-group is still empty, it will
220   * be deleted; otherwise, the sub-group will be added as a new command
221   * into parent group.
222   * A matching begin() must have been called previously.
223   */
224   void end();
225 
226   /**
227   * Abort the current command sub-group being compiled.  All commands
228   * already added to the sub-groups undo list will be discarded.
229   * Intermediate command groups will be left intact.
230   */
231   void abort();
232 
233   /**
234   * Undo last command. This will move the command to the redo list.
235   */
236   virtual void undo();
237 
238   /**
239   * Redo next command. This will move the command back to the undo list.
240   */
241   virtual void redo();
242 
243   /// Undo all commands
244   void undoAll();
245 
246   /// Redo all commands
247   void redoAll();
248 
249   /// Revert to marked
250   void revert();
251 
252   /// Can we undo more commands
253   FXbool canUndo() const;
254 
255   /// Can we redo more commands
256   FXbool canRedo() const;
257 
258   /// Can revert to marked
259   FXbool canRevert() const;
260 
261   /**
262   * Return true if currently inside undo or redo operation; this
263   * is useful to avoid generating another undo command while inside
264   * an undo operation.
265   */
busy()266   FXbool busy() const { return working; }
267 
268   /// Current top level undo command
current()269   FXCommand* current() const { return undolist; }
270 
271   /**
272   * Return name of the first undo command available; if no
273   * undo command available this will return the empty string.
274   */
275   virtual FXString undoName() const;
276 
277   /**
278   * Return name of the first redo command available; if no
279   * Redo command available this will return the empty string.
280   */
281   virtual FXString redoName() const;
282 
283   /// Number of undo records
undoCount()284   FXint undoCount() const { return undocount; }
285 
286   /// Number of redo records
redoCount()287   FXint redoCount() const { return redocount; }
288 
289   /// Size of undo information
290   virtual FXuint size() const;
291 
292   /**
293   * Clear list, and unmark all states.
294   * All undo and redo information will be destroyed.
295   */
296   void clear();
297 
298   /**
299   * Trim undo list down to at most nc commands.
300   * Call this periodically to prevent the undo-list from growing
301   * beyond a certain number of records.
302   */
303   void trimCount(FXint nc);
304 
305   /**
306   * Trim undo list down to at most size sz.
307   * Call this periodically to prevent the undo-list from growing
308   * beyond a certain amount of memory.
309   */
310   void trimSize(FXuint sz);
311 
312   /**
313   * Mark the current state of the undo list, which is initially unmarked.
314   * There can be only one active mark at any time.  Call mark() at any
315   * time when you know the document to be "clean"; for example when you
316   * save the document to disk.
317   */
318   void mark();
319 
320   /**
321   * Unmark all states in the undo list.
322   */
323   void unmark();
324 
325   /**
326   * Check if the current state was marked, if the application has returned
327   * to the previously marked state.
328   */
329   FXbool marked() const;
330   };
331 
332 
333 }
334 
335 #endif
336