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