1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Sonic Visualiser
5     An audio file viewer and annotation editor.
6     Centre for Digital Music, Queen Mary, University of London.
7 
8     This program is free software; you can redistribute it and/or
9     modify it under the terms of the GNU General Public License as
10     published by the Free Software Foundation; either version 2 of the
11     License, or (at your option) any later version.  See the file
12     COPYING included with this distribution for more information.
13 */
14 
15 /*
16    This is a modified version of a source file from the Rosegarden
17    MIDI and audio sequencer and notation editor, copyright 2000-2006
18    Chris Cannam, distributed under the GNU General Public License.
19 
20    This file contains traces of the KCommandHistory class from the KDE
21    project, copyright 2000 Werner Trobin and David Faure and
22    distributed under the GNU Lesser General Public License.
23 */
24 
25 #ifndef SV_MULTI_VIEW_COMMAND_HISTORY_H
26 #define SV_MULTI_VIEW_COMMAND_HISTORY_H
27 
28 #include <QObject>
29 #include <QString>
30 
31 #include <stack>
32 #include <set>
33 #include <map>
34 
35 class Command;
36 class MacroCommand;
37 class QAction;
38 class QMenu;
39 class QToolBar;
40 class QTimer;
41 
42 /**
43  * The CommandHistory class stores a list of executed commands and
44  * maintains Undo and Redo actions synchronised with those commands.
45  *
46  * CommandHistory allows you to associate more than one Undo and Redo
47  * menu or toolbar with the same command history, and it keeps them
48  * all up-to-date at once.  This makes it effective in systems where
49  * multiple views may be editing the same data.
50  */
51 
52 class CommandHistory : public QObject
53 {
54     Q_OBJECT
55 
56 public:
57     virtual ~CommandHistory();
58 
59     static CommandHistory *getInstance();
60 
61     void clear();
62 
63     void registerMenu(QMenu *menu);
64     void registerToolbar(QToolBar *toolbar);
65 
66     /**
67      * Add a command to the command history.
68      *
69      * The command will normally be executed before being added; but
70      * if a compound operation is in use (see startCompoundOperation
71      * below), the execute status of the compound operation will
72      * determine whether the command is executed or not.
73      */
74     void addCommand(Command *command);
75 
76     /**
77      * Add a command to the command history.
78      *
79      * If execute is true, the command will be executed before being
80      * added.  Otherwise it will be assumed to have been already
81      * executed -- a command should not be added to the history unless
82      * its work has actually been done somehow!
83      *
84      * If a compound operation is in use (see startCompoundOperation
85      * below), the execute value passed to this method will override
86      * the execute status of the compound operation.  In this way it's
87      * possible to have a compound operation mixing both to-execute
88      * and pre-executed commands.
89      *
90      * If bundle is true, the command will be a candidate for bundling
91      * with any adjacent bundleable commands that have the same name,
92      * into a single compound command.  This is useful for small
93      * commands that may be executed repeatedly altering the same data
94      * (e.g. type text, set a parameter) whose number and extent is
95      * not known in advance.  The bundle parameter will be ignored if
96      * a compound operation is already in use.
97      */
98     void addCommand(Command *command, bool execute, bool bundle = false);
99 
100     /// Return the maximum number of items in the undo history.
getUndoLimit()101     int getUndoLimit() const { return m_undoLimit; }
102 
103     /// Set the maximum number of items in the undo history.
104     void setUndoLimit(int limit);
105 
106     /// Return the maximum number of items in the redo history.
getRedoLimit()107     int getRedoLimit() const { return m_redoLimit; }
108 
109     /// Set the maximum number of items in the redo history.
110     void setRedoLimit(int limit);
111 
112     /// Return the maximum number of items visible in undo and redo menus.
getMenuLimit()113     int getMenuLimit() const { return m_menuLimit; }
114 
115     /// Set the maximum number of items in the menus.
116     void setMenuLimit(int limit);
117 
118     /// Return the time after which a bundle will be closed if nothing is added.
getBundleTimeout()119     int getBundleTimeout() const { return m_bundleTimeout; }
120 
121     /// Set the time after which a bundle will be closed if nothing is added.
122     void setBundleTimeout(int msec);
123 
124     /// Start recording commands to batch up into a single compound command.
125     void startCompoundOperation(QString name, bool execute);
126 
127     /// Finish recording commands and store the compound command.
128     void endCompoundOperation();
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 whenever a command has just been executed or
162      * unexecuted, whether by addCommand, undo, or redo.
163      */
164     void commandExecuted();
165 
166     /**
167      * Emitted whenever a command has just been executed, whether by
168      * addCommand or redo.
169      */
170     void commandExecuted(Command *);
171 
172     /**
173      * Emitted whenever a command has just been unexecuted, whether by
174      * addCommand or undo.
175      */
176     void commandUnexecuted(Command *);
177 
178     /**
179      * Emitted when the undo/redo stack has reached the same state at
180      * which the documentSaved slot was last called.
181      */
182     void documentRestored();
183 
184     /**
185      * Emitted when some activity happened (for activity logging).
186      */
187     void activity(QString);
188 
189 protected:
190     CommandHistory();
191     static CommandHistory *m_instance;
192 
193     QAction *m_undoAction;
194     QAction *m_redoAction;
195     QAction *m_undoMenuAction;
196     QAction *m_redoMenuAction;
197     QMenu *m_undoMenu;
198     QMenu *m_redoMenu;
199 
200     std::map<QAction *, int> m_actionCounts;
201 
202     typedef std::stack<Command *> CommandStack;
203     CommandStack m_undoStack;
204     CommandStack m_redoStack;
205 
206     int m_undoLimit;
207     int m_redoLimit;
208     int m_menuLimit;
209     int m_savedAt;
210 
211     MacroCommand *m_currentCompound;
212     bool m_executeCompound;
213     void addToCompound(Command *command, bool execute);
214 
215     MacroCommand *m_currentBundle;
216     bool m_bundling;
217     QString m_currentBundleName;
218     QTimer *m_bundleTimer;
219     int m_bundleTimeout;
220     void addToBundle(Command *command, bool execute);
221     void closeBundle();
222 
223     void updateActions();
224 
225     void clipCommands();
226 
227     void clipStack(CommandStack &stack, int limit);
228     void clearStack(CommandStack &stack);
229 };
230 
231 
232 #endif
233