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