comparison base/CommandHistory.cpp @ 17:2fb933f88604

* Update some copyright notice dates * Add commands for basic editing operations in time-instants and time-value layers
author Chris Cannam
date Tue, 31 Jan 2006 15:57:25 +0000
parents base/MultiViewCommandHistory.cpp@cc98d496d52b
children 2b6412c1e724
comparison
equal deleted inserted replaced
16:cc98d496d52b 17:2fb933f88604
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 "CommandHistory.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 CommandHistory *CommandHistory::m_instance = 0;
32
33 CommandHistory::CommandHistory() :
34 m_undoLimit(50),
35 m_redoLimit(50),
36 m_savedAt(0)
37 {
38 m_undoAction = new QAction(QIcon(":/icons/undo.png"), tr("&Undo"), this);
39 m_undoAction->setShortcut(tr("Ctrl+Z"));
40 connect(m_undoAction, SIGNAL(triggered()), this, SLOT(undo()));
41
42 m_undoMenuAction = new QAction(QIcon(":/icons/undo.png"), tr("&Undo"), this);
43 connect(m_undoMenuAction, SIGNAL(triggered()), this, SLOT(undo()));
44
45 m_undoMenu = new QMenu(tr("&Undo"));
46 m_undoMenuAction->setMenu(m_undoMenu);
47 connect(m_undoMenu, SIGNAL(triggered(QAction *)),
48 this, SLOT(undoActivated(QAction*)));
49
50 m_redoAction = new QAction(QIcon(":/icons/redo.png"), tr("Re&do"), this);
51 m_redoAction->setShortcut(tr("Ctrl+Shift+Z"));
52 connect(m_redoAction, SIGNAL(triggered()), this, SLOT(redo()));
53
54 m_redoMenuAction = new QAction(QIcon(":/icons/redo.png"), tr("Re&do"), this);
55 connect(m_redoMenuAction, SIGNAL(triggered()), this, SLOT(redo()));
56
57 m_redoMenu = new QMenu(tr("Re&do"));
58 m_redoMenuAction->setMenu(m_redoMenu);
59 connect(m_redoMenu, SIGNAL(triggered(QAction *)),
60 this, SLOT(redoActivated(QAction*)));
61 }
62
63 CommandHistory::~CommandHistory()
64 {
65 m_savedAt = -1;
66 clearStack(m_undoStack);
67 clearStack(m_redoStack);
68
69 delete m_undoMenu;
70 delete m_redoMenu;
71 }
72
73 CommandHistory *
74 CommandHistory::getInstance()
75 {
76 if (!m_instance) m_instance = new CommandHistory();
77 return m_instance;
78 }
79
80 void
81 CommandHistory::clear()
82 {
83 m_savedAt = -1;
84 clearStack(m_undoStack);
85 clearStack(m_redoStack);
86 updateActions();
87 }
88
89 void
90 CommandHistory::registerMenu(QMenu *menu)
91 {
92 menu->addAction(m_undoAction);
93 menu->addAction(m_redoAction);
94 }
95
96 void
97 CommandHistory::registerToolbar(QToolBar *toolbar)
98 {
99 toolbar->addAction(m_undoMenuAction);
100 toolbar->addAction(m_redoMenuAction);
101 }
102
103 void
104 CommandHistory::addCommand(Command *command, bool execute)
105 {
106 if (!command) return;
107
108 std::cerr << "MVCH::addCommand: " << command->getName().toLocal8Bit().data() << std::endl;
109
110 // We can't redo after adding a command
111 clearStack(m_redoStack);
112
113 // can we reach savedAt?
114 if ((int)m_undoStack.size() < m_savedAt) m_savedAt = -1; // nope
115
116 m_undoStack.push(command);
117 clipCommands();
118
119 if (execute) {
120 command->execute();
121 }
122
123 // Emit even if we aren't executing the command, because
124 // someone must have executed it for this to make any sense
125 emit commandExecuted();
126 emit commandExecuted(command);
127
128 updateActions();
129 }
130
131 void
132 CommandHistory::addExecutedCommand(Command *command)
133 {
134 addCommand(command, false);
135 }
136
137 void
138 CommandHistory::addCommandAndExecute(Command *command)
139 {
140 addCommand(command, true);
141 }
142
143 void
144 CommandHistory::undo()
145 {
146 if (m_undoStack.empty()) return;
147
148 Command *command = m_undoStack.top();
149 command->unexecute();
150 emit commandExecuted();
151 emit commandUnexecuted(command);
152
153 m_redoStack.push(command);
154 m_undoStack.pop();
155
156 clipCommands();
157 updateActions();
158
159 if ((int)m_undoStack.size() == m_savedAt) emit documentRestored();
160 }
161
162 void
163 CommandHistory::redo()
164 {
165 if (m_redoStack.empty()) return;
166
167 Command *command = m_redoStack.top();
168 command->execute();
169 emit commandExecuted();
170 emit commandExecuted(command);
171
172 m_undoStack.push(command);
173 m_redoStack.pop();
174 // no need to clip
175
176 updateActions();
177 }
178
179 void
180 CommandHistory::setUndoLimit(int limit)
181 {
182 if (limit > 0 && limit != m_undoLimit) {
183 m_undoLimit = limit;
184 clipCommands();
185 }
186 }
187
188 void
189 CommandHistory::setRedoLimit(int limit)
190 {
191 if (limit > 0 && limit != m_redoLimit) {
192 m_redoLimit = limit;
193 clipCommands();
194 }
195 }
196
197 void
198 CommandHistory::documentSaved()
199 {
200 m_savedAt = m_undoStack.size();
201 }
202
203 void
204 CommandHistory::clipCommands()
205 {
206 if ((int)m_undoStack.size() > m_undoLimit) {
207 m_savedAt -= (m_undoStack.size() - m_undoLimit);
208 }
209
210 clipStack(m_undoStack, m_undoLimit);
211 clipStack(m_redoStack, m_redoLimit);
212 }
213
214 void
215 CommandHistory::clipStack(CommandStack &stack, int limit)
216 {
217 int i;
218
219 if ((int)stack.size() > limit) {
220
221 CommandStack tempStack;
222
223 for (i = 0; i < limit; ++i) {
224 Command *command = stack.top();
225 std::cerr << "MVCH::clipStack: Saving recent command: " << command->getName().toLocal8Bit().data() << " at " << command << std::endl;
226 tempStack.push(stack.top());
227 stack.pop();
228 }
229
230 clearStack(stack);
231
232 for (i = 0; i < m_undoLimit; ++i) {
233 stack.push(tempStack.top());
234 tempStack.pop();
235 }
236 }
237 }
238
239 void
240 CommandHistory::clearStack(CommandStack &stack)
241 {
242 while (!stack.empty()) {
243 Command *command = stack.top();
244 std::cerr << "MVCH::clearStack: About to delete command: " << command->getName().toLocal8Bit().data() << " at " << command << std::endl;
245 delete command;
246 stack.pop();
247 }
248 }
249
250 void
251 CommandHistory::undoActivated(QAction *action)
252 {
253 int pos = m_actionCounts[action];
254 for (int i = 0; i <= pos; ++i) {
255 undo();
256 }
257 }
258
259 void
260 CommandHistory::redoActivated(QAction *action)
261 {
262 int pos = m_actionCounts[action];
263 for (int i = 0; i <= pos; ++i) {
264 redo();
265 }
266 }
267
268 void
269 CommandHistory::updateActions()
270 {
271 m_actionCounts.clear();
272
273 for (int undo = 0; undo <= 1; ++undo) {
274
275 QAction *action(undo ? m_undoAction : m_redoAction);
276 QAction *menuAction(undo ? m_undoMenuAction : m_redoMenuAction);
277 QMenu *menu(undo ? m_undoMenu : m_redoMenu);
278 CommandStack &stack(undo ? m_undoStack : m_redoStack);
279
280 if (stack.empty()) {
281
282 QString text(undo ? tr("Nothing to undo") : tr("Nothing to redo"));
283
284 action->setEnabled(false);
285 action->setText(text);
286
287 menuAction->setEnabled(false);
288 menuAction->setText(text);
289
290 } else {
291
292 action->setEnabled(true);
293 menuAction->setEnabled(true);
294
295 QString commandName = stack.top()->getName();
296 commandName.replace(QRegExp("&"), "");
297
298 QString text = (undo ? tr("&Undo %1") : tr("Re&do %1"))
299 .arg(commandName);
300
301 action->setText(text);
302 menuAction->setText(text);
303 }
304
305 menu->clear();
306
307 CommandStack tempStack;
308 int j = 0;
309
310 while (j < 10 && !stack.empty()) {
311
312 Command *command = stack.top();
313 tempStack.push(command);
314 stack.pop();
315
316 QString commandName = command->getName();
317 commandName.replace(QRegExp("&"), "");
318
319 QString text;
320 if (undo) text = tr("&Undo %1").arg(commandName);
321 else text = tr("Re&do %1").arg(commandName);
322
323 QAction *action = menu->addAction(text);
324 m_actionCounts[action] = j++;
325 }
326
327 while (!tempStack.empty()) {
328 stack.push(tempStack.top());
329 tempStack.pop();
330 }
331 }
332 }
333