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