1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 2 3 /* 4 Rosegarden 5 A MIDI and audio sequencer and musical notation editor. 6 Copyright 2000-2021 the Rosegarden development team. 7 8 Other copyrights also apply to some parts of this work. Please 9 see the AUTHORS file and individual file headers for details. 10 11 This program is free software; you can redistribute it and/or 12 modify it under the terms of the GNU General Public License as 13 published by the Free Software Foundation; either version 2 of the 14 License, or (at your option) any later version. See the file 15 COPYING included with this distribution for more information. 16 */ 17 18 #ifndef RG_COMMANDHISTORY_H 19 #define RG_COMMANDHISTORY_H 20 21 #include <QObject> 22 #include <QString> 23 24 #include <stack> 25 #include <set> 26 #include <map> 27 28 class QAction; 29 class QMenu; 30 class QToolBar; 31 class QTimer; 32 33 namespace Rosegarden 34 { 35 36 class Command; 37 class MacroCommand; 38 class ActionFileClient; 39 40 /** 41 * The CommandHistory class stores a list of executed 42 * commands and maintains Undo and Redo actions synchronised with 43 * those commands. 44 * 45 * CommandHistory allows you to associate more than one Undo 46 * and Redo menu or toolbar with the same command history, and it 47 * keeps them all up-to-date at once. This makes it effective in 48 * systems where multiple views may be editing the same data. 49 */ 50 class CommandHistory : public QObject 51 { 52 Q_OBJECT 53 54 public: 55 ~CommandHistory() override; 56 57 static CommandHistory *getInstance(); 58 59 void clear(); 60 61 // These are done by the .rc file these days. 62 //void registerMenu(QMenu *menu); 63 //void registerToolbar(QToolBar *toolbar); 64 65 /// Add a command to the command history. 66 /** 67 * The command will normally be executed before being added; but 68 * if a compound operation is in use (see startCompoundOperation 69 * below), the execute status of the compound operation will 70 * determine whether the command is executed or not. 71 */ 72 void addCommand(Command *command); 73 74 /// Add a command to the command history. 75 /** 76 * If execute is true, the command will be executed before being 77 * added. Otherwise it will be assumed to have been already 78 * executed -- a command should not be added to the history unless 79 * its work has actually been done somehow! 80 * 81 * If a compound operation is in use (see startCompoundOperation 82 * below), the execute value passed to this method will override 83 * the execute status of the compound operation. In this way it's 84 * possible to have a compound operation mixing both to-execute 85 * and pre-executed commands. 86 * 87 * If bundle is true, the command will be a candidate for bundling 88 * with any adjacent bundleable commands that have the same name, 89 * into a single compound command. This is useful for small 90 * commands that may be executed repeatedly altering the same data 91 * (e.g. type text, set a parameter) whose number and extent is 92 * not known in advance. The bundle parameter will be ignored if 93 * a compound operation is already in use. 94 */ 95 void addCommand(Command *command, bool execute, bool bundle = false); 96 97 /// Return the maximum number of items in the undo history. 98 int getUndoLimit() const { return m_undoLimit; } 99 100 /// Set the maximum number of items in the undo history. 101 void setUndoLimit(int limit); 102 103 /// Return the maximum number of items in the redo history. 104 int getRedoLimit() const { return m_redoLimit; } 105 106 /// Set the maximum number of items in the redo history. 107 void setRedoLimit(int limit); 108 109 /// Return the maximum number of items visible in undo and redo menus. 110 int getMenuLimit() const { return m_menuLimit; } 111 112 /// Set the maximum number of items in the menus. 113 void setMenuLimit(int limit); 114 115 /// Return the time after which a bundle will be closed if nothing is added. 116 int getBundleTimeout() const { return m_bundleTimeout; } 117 118 /// Set the time after which a bundle will be closed if nothing is added. 119 void setBundleTimeout(int msec); 120 121 /// Start recording commands to batch up into a single compound command. 122 void startCompoundOperation(QString name, bool execute); 123 124 /// Finish recording commands and store the compound command. 125 void endCompoundOperation(); 126 127 /// Enable/Disable undo (during playback). 128 void enableUndo(bool enable); 129 130 public slots: 131 /** 132 * Checkpoint function that should be called when the document is 133 * saved. If the undo/redo stack later returns to the point at 134 * which the document was saved, the documentRestored signal will 135 * be emitted. 136 */ 137 virtual void documentSaved(); 138 139 /** 140 * Add a command to the history that has already been executed, 141 * without executing it again. Equivalent to addCommand(command, false). 142 */ 143 void addExecutedCommand(Command *); 144 145 /** 146 * Add a command to the history and also execute it. Equivalent 147 * to addCommand(command, true). 148 */ 149 void addCommandAndExecute(Command *); 150 151 void undo(); 152 void redo(); 153 154 protected slots: 155 void undoActivated(QAction *); 156 void redoActivated(QAction *); 157 void bundleTimerTimeout(); 158 159 signals: 160 /** 161 * Emitted just before commandExecuted() so that linked segments can 162 * update their siblings. 163 */ 164 void updateLinkedSegments(Command *); 165 166 /** 167 * Emitted whenever a command has just been executed or 168 * unexecuted, whether by addCommand, undo, or redo. Note in 169 * particular that this is emitted by both undo and redo. 170 */ 171 void commandExecuted(); 172 173 /** 174 * Emitted whenever a command has just been executed, whether by 175 * addCommand or redo. Note that this is not emitted by undo, 176 * which emits commandUnexecuted(Command *) instead. 177 */ 178 void commandExecuted(Command *); 179 180 /** 181 * Emitted whenever a command has just been unexecuted, whether by 182 * addCommand or undo. 183 */ 184 void commandUnexecuted(Command *); 185 186 /** 187 * Emitted when the undo/redo stack has reached the same state at 188 * which the documentSaved slot was last called. 189 */ 190 void documentRestored(); 191 192 193 protected: 194 CommandHistory(); 195 static CommandHistory *m_instance; 196 197 // Actions and Menus 198 199 /// Edit > Undo on all menus. 200 QAction *m_undoAction; 201 /// Edit > Redo on all menus. 202 QAction *m_redoAction; 203 /// RosegardenMainWindow toolbar undo. 204 QAction *m_undoMenuAction; 205 /// RosegardenMainWindow toolbar redo. 206 QAction *m_redoMenuAction; 207 208 QMenu *m_undoMenu; 209 QMenu *m_redoMenu; 210 211 std::map<QAction *, int> m_actionCounts; 212 213 void updateActions(); 214 215 // Command Stacks 216 typedef std::stack<Command *> CommandStack; 217 CommandStack m_undoStack; 218 CommandStack m_redoStack; 219 void clipStack(CommandStack &stack, int limit); 220 void clearStack(CommandStack &stack); 221 void clipCommands(); 222 223 int m_undoLimit; 224 int m_redoLimit; 225 int m_menuLimit; 226 int m_savedAt; 227 228 // Compound 229 MacroCommand *m_currentCompound; 230 bool m_executeCompound; 231 void addToCompound(Command *command, bool execute); 232 233 // Bundle 234 MacroCommand *m_currentBundle; 235 QString m_currentBundleName; 236 QTimer *m_bundleTimer; 237 int m_bundleTimeout; 238 void addToBundle(Command *command, bool execute); 239 void closeBundle(); 240 241 /// Enable/Disable undo (during playback). 242 bool m_enableUndo; 243 244 }; 245 246 } 247 248 #endif 249