comparison base/MultiViewCommandHistory.cpp @ 16:cc98d496d52b

* Add command history class, and basic undo/redo menus. No actual commands to undo/redo yet. Selecting the placeholders sometimes seems to cause a crash, so this looks a little uncertain so far. * Add Rename Layer * Remove models from playback when their layers are removed (and ref counts hit zero) * Don't hang around waiting so much when there's work to be done in the audio buffer fill thread * Put more sensible names on layers generated from transforms * Add basic editing to time-value layer like existing editing in time-instants layer, and make both of them snap to the appropriate resolution during drag
author Chris Cannam
date Mon, 30 Jan 2006 17:51:56 +0000
parents
children
comparison
equal deleted inserted replaced
15:47500c27ac26 16:cc98d496d52b
1 /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 A waveform viewer and audio annotation editor.
5 Chris Cannam, Queen Mary University of London, 2005-2006
6
7 This is experimental software. Not for distribution.
8 */
9
10 /*
11 This is a modified version of a source file from the Rosegarden
12 MIDI and audio sequencer and notation editor, copyright 2000-2006
13 Chris Cannam, distributed under the GNU General Public License.
14
15 This file contains traces of the KCommandHistory class from the KDE
16 project, copyright 2000 Werner Trobin and David Faure and
17 distributed under the GNU Lesser General Public License.
18 */
19
20 #include "MultiViewCommandHistory.h"
21
22 #include "Command.h"
23
24 #include <QRegExp>
25 #include <QMenu>
26 #include <QToolBar>
27 #include <QString>
28
29 #include <iostream>
30
31 MultiViewCommandHistory::MultiViewCommandHistory() :
32 m_undoLimit(50),
33 m_redoLimit(50),
34 m_savedAt(0)
35 {
36 m_undoAction = new QAction(QIcon(":/icons/undo.png"), tr("&Undo"), this);
37 m_undoAction->setShortcut(tr("Ctrl+Z"));
38 connect(m_undoAction, SIGNAL(triggered()), this, SLOT(undo()));
39
40 m_undoMenu = new QMenu(tr("&Undo"));
41 m_undoAction->setMenu(m_undoMenu);
42 connect(m_undoMenu, SIGNAL(triggered(QAction *)),
43 this, SLOT(undoActivated(QAction*)));
44
45 m_redoAction = new QAction(QIcon(":/icons/redo.png"), tr("&Redo"), this);
46 m_redoAction->setShortcut(tr("Ctrl+Shift+Z"));
47 connect(m_redoAction, SIGNAL(triggered()), this, SLOT(redo()));
48
49 m_redoMenu = new QMenu(tr("Re&do"));
50 m_redoAction->setMenu(m_redoMenu);
51 connect(m_redoMenu, SIGNAL(triggered(QAction *)),
52 this, SLOT(redoActivated(QAction*)));
53 }
54
55 MultiViewCommandHistory::~MultiViewCommandHistory()
56 {
57 m_savedAt = -1;
58 clearStack(m_undoStack);
59 clearStack(m_redoStack);
60
61 delete m_undoMenu;
62 delete m_redoMenu;
63 }
64
65 void
66 MultiViewCommandHistory::clear()
67 {
68 m_savedAt = -1;
69 clearStack(m_undoStack);
70 clearStack(m_redoStack);
71 updateActions();
72 }
73
74 void
75 MultiViewCommandHistory::registerMenu(QMenu *menu)
76 {
77 menu->addAction(m_undoAction);
78 menu->addAction(m_redoAction);
79 }
80
81 void
82 MultiViewCommandHistory::registerToolbar(QToolBar *toolbar)
83 {
84 toolbar->addAction(m_undoAction);
85 toolbar->addAction(m_redoAction);
86 }
87
88 void
89 MultiViewCommandHistory::addCommand(Command *command, bool execute)
90 {
91 if (!command) return;
92
93 std::cerr << "MVCH::addCommand: " << command->name().toLocal8Bit().data() << std::endl;
94
95 // We can't redo after adding a command
96 clearStack(m_redoStack);
97
98 // can we reach savedAt?
99 if ((int)m_undoStack.size() < m_savedAt) m_savedAt = -1; // nope
100
101 m_undoStack.push(command);
102 clipCommands();
103
104 if (execute) {
105 command->execute();
106 emit commandExecuted();
107 emit commandExecuted(command);
108 }
109
110 // updateButtons();
111 updateActions();
112 }
113
114 void
115 MultiViewCommandHistory::undo()
116 {
117 if (m_undoStack.empty()) return;
118
119 Command *command = m_undoStack.top();
120 command->unexecute();
121 emit commandExecuted();
122 emit commandExecuted(command);
123
124 m_redoStack.push(command);
125 m_undoStack.pop();
126
127 clipCommands();
128 updateActions();
129
130 if ((int)m_undoStack.size() == m_savedAt) emit documentRestored();
131 }
132
133 void
134 MultiViewCommandHistory::redo()
135 {
136 if (m_redoStack.empty()) return;
137
138 Command *command = m_redoStack.top();
139 command->execute();
140 emit commandExecuted();
141 emit commandExecuted(command);
142
143 m_undoStack.push(command);
144 m_redoStack.pop();
145 // no need to clip
146
147 updateActions();
148 }
149
150 void
151 MultiViewCommandHistory::setUndoLimit(int limit)
152 {
153 if (limit > 0 && limit != m_undoLimit) {
154 m_undoLimit = limit;
155 clipCommands();
156 }
157 }
158
159 void
160 MultiViewCommandHistory::setRedoLimit(int limit)
161 {
162 if (limit > 0 && limit != m_redoLimit) {
163 m_redoLimit = limit;
164 clipCommands();
165 }
166 }
167
168 void
169 MultiViewCommandHistory::documentSaved()
170 {
171 m_savedAt = m_undoStack.size();
172 }
173
174 void
175 MultiViewCommandHistory::clipCommands()
176 {
177 if ((int)m_undoStack.size() > m_undoLimit) {
178 m_savedAt -= (m_undoStack.size() - m_undoLimit);
179 }
180
181 clipStack(m_undoStack, m_undoLimit);
182 clipStack(m_redoStack, m_redoLimit);
183 }
184
185 void
186 MultiViewCommandHistory::clipStack(CommandStack &stack, int limit)
187 {
188 int i;
189
190 if ((int)stack.size() > limit) {
191
192 CommandStack tempStack;
193
194 for (i = 0; i < limit; ++i) {
195 Command *command = stack.top();
196 std::cerr << "MVCH::clipStack: Saving recent command: " << command->name().toLocal8Bit().data() << " at " << command << std::endl;
197 tempStack.push(stack.top());
198 stack.pop();
199 }
200
201 clearStack(stack);
202
203 for (i = 0; i < m_undoLimit; ++i) {
204 stack.push(tempStack.top());
205 tempStack.pop();
206 }
207 }
208 }
209
210 void
211 MultiViewCommandHistory::clearStack(CommandStack &stack)
212 {
213 while (!stack.empty()) {
214 Command *command = stack.top();
215 std::cerr << "MVCH::clearStack: About to delete command: " << command->name().toLocal8Bit().data() << " at " << command << std::endl;
216 delete command;
217 stack.pop();
218 }
219 }
220
221 void
222 MultiViewCommandHistory::undoActivated(QAction *action)
223 {
224 int pos = m_actionCounts[action];
225 for (int i = 0; i <= pos; ++i) {
226 undo();
227 }
228 }
229
230 void
231 MultiViewCommandHistory::redoActivated(QAction *action)
232 {
233 int pos = m_actionCounts[action];
234 for (int i = 0; i <= pos; ++i) {
235 redo();
236 }
237 }
238
239 void
240 MultiViewCommandHistory::updateActions()
241 {
242 if (m_undoStack.empty()) {
243 m_undoAction->setEnabled(false);
244 m_undoAction->setText(tr("Nothing to undo"));
245 } else {
246 m_undoAction->setEnabled(true);
247 QString commandName = m_undoStack.top()->name();
248 commandName.replace(QRegExp("&"), "");
249 QString text = tr("&Undo %1").arg(commandName);
250 m_undoAction->setText(text);
251 }
252
253 if (m_redoStack.empty()) {
254 m_redoAction->setEnabled(false);
255 m_redoAction->setText(tr("Nothing to redo"));
256 } else {
257 m_redoAction->setEnabled(true);
258 QString commandName = m_redoStack.top()->name();
259 commandName.replace(QRegExp("&"), "");
260 QString text = tr("Re&do %1").arg(commandName);
261 m_redoAction->setText(text);
262 }
263
264 m_actionCounts.clear();
265
266 for (int undo = 0; undo <= 1; ++undo) {
267
268 QMenu *menu(undo ? m_undoMenu : m_redoMenu);
269 CommandStack &stack(undo ? m_undoStack : m_redoStack);
270
271 menu->clear();
272
273 CommandStack tempStack;
274 int j = 0;
275
276 while (j < 10 && !stack.empty()) {
277
278 Command *command = stack.top();
279 tempStack.push(command);
280 stack.pop();
281
282 QString commandName = command->name();
283 commandName.replace(QRegExp("&"), "");
284
285 QString text;
286 if (undo) text = tr("&Undo %1").arg(commandName);
287 else text = tr("Re&do %1").arg(commandName);
288
289 QAction *action = menu->addAction(text);
290 m_actionCounts[action] = j++;
291 }
292
293 while (!tempStack.empty()) {
294 stack.push(tempStack.top());
295 tempStack.pop();
296 }
297 }
298 }
299