annotate base/CommandHistory.cpp @ 33:51e158b505da

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