Mercurial > hg > svcore
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 |