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