Chris@376: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@376: 
Chris@376: /*
Chris@376:     Sonic Visualiser
Chris@376:     An audio file viewer and annotation editor.
Chris@376:     Centre for Digital Music, Queen Mary, University of London.
Chris@376:     
Chris@376:     This program is free software; you can redistribute it and/or
Chris@376:     modify it under the terms of the GNU General Public License as
Chris@376:     published by the Free Software Foundation; either version 2 of the
Chris@376:     License, or (at your option) any later version.  See the file
Chris@376:     COPYING included with this distribution for more information.
Chris@376: */
Chris@376: 
Chris@376: /*
Chris@376:    This is a modified version of a source file from the Rosegarden
Chris@376:    MIDI and audio sequencer and notation editor, copyright 2000-2006
Chris@376:    Chris Cannam, distributed under the GNU General Public License.
Chris@376: 
Chris@376:    This file contains traces of the KCommandHistory class from the KDE
Chris@376:    project, copyright 2000 Werner Trobin and David Faure and
Chris@376:    distributed under the GNU Lesser General Public License.
Chris@376: */
Chris@376: 
Chris@376: #ifndef _MULTI_VIEW_COMMAND_HISTORY_H_
Chris@376: #define _MULTI_VIEW_COMMAND_HISTORY_H_
Chris@376: 
Chris@376: #include <QObject>
Chris@376: #include <QString>
Chris@376: 
Chris@376: #include <stack>
Chris@376: #include <set>
Chris@376: #include <map>
Chris@376: 
Chris@376: class Command;
Chris@376: class MacroCommand;
Chris@376: class QAction;
Chris@376: class QMenu;
Chris@376: class QToolBar;
Chris@376: class QTimer;
Chris@376: 
Chris@376: /**
Chris@376:  * The CommandHistory class stores a list of executed commands and
Chris@376:  * maintains Undo and Redo actions synchronised with those commands.
Chris@376:  *
Chris@376:  * CommandHistory allows you to associate more than one Undo and Redo
Chris@376:  * menu or toolbar with the same command history, and it keeps them
Chris@376:  * all up-to-date at once.  This makes it effective in systems where
Chris@376:  * multiple views may be editing the same data.
Chris@376:  */
Chris@376: 
Chris@376: class CommandHistory : public QObject
Chris@376: {
Chris@376:     Q_OBJECT
Chris@376: 
Chris@376: public:
Chris@376:     virtual ~CommandHistory();
Chris@376: 
Chris@376:     static CommandHistory *getInstance();
Chris@376: 
Chris@376:     void clear();
Chris@376:     
Chris@376:     void registerMenu(QMenu *menu);
Chris@376:     void registerToolbar(QToolBar *toolbar);
Chris@376: 
Chris@376:     /**
Chris@376:      * Add a command to the command history.
Chris@376:      * 
Chris@376:      * The command will normally be executed before being added; but
Chris@376:      * if a compound operation is in use (see startCompoundOperation
Chris@376:      * below), the execute status of the compound operation will
Chris@376:      * determine whether the command is executed or not.
Chris@376:      */
Chris@376:     void addCommand(Command *command);
Chris@376:     
Chris@376:     /**
Chris@376:      * Add a command to the command history.
Chris@376:      *
Chris@376:      * If execute is true, the command will be executed before being
Chris@376:      * added.  Otherwise it will be assumed to have been already
Chris@376:      * executed -- a command should not be added to the history unless
Chris@376:      * its work has actually been done somehow!
Chris@376:      *
Chris@376:      * If a compound operation is in use (see startCompoundOperation
Chris@376:      * below), the execute value passed to this method will override
Chris@376:      * the execute status of the compound operation.  In this way it's
Chris@376:      * possible to have a compound operation mixing both to-execute
Chris@376:      * and pre-executed commands.
Chris@376:      *
Chris@376:      * If bundle is true, the command will be a candidate for bundling
Chris@376:      * with any adjacent bundleable commands that have the same name,
Chris@376:      * into a single compound command.  This is useful for small
Chris@376:      * commands that may be executed repeatedly altering the same data
Chris@376:      * (e.g. type text, set a parameter) whose number and extent is
Chris@376:      * not known in advance.  The bundle parameter will be ignored if
Chris@376:      * a compound operation is already in use.
Chris@376:      */
Chris@376:     void addCommand(Command *command, bool execute, bool bundle = false);
Chris@376:     
Chris@376:     /// Return the maximum number of items in the undo history.
Chris@376:     int getUndoLimit() const { return m_undoLimit; }
Chris@376: 
Chris@376:     /// Set the maximum number of items in the undo history.
Chris@376:     void setUndoLimit(int limit);
Chris@376: 
Chris@376:     /// Return the maximum number of items in the redo history.
Chris@376:     int getRedoLimit() const { return m_redoLimit; }
Chris@376: 
Chris@376:     /// Set the maximum number of items in the redo history.
Chris@376:     void setRedoLimit(int limit);
Chris@376:     
Chris@376:     /// Return the maximum number of items visible in undo and redo menus.
Chris@376:     int getMenuLimit() const { return m_menuLimit; }
Chris@376: 
Chris@376:     /// Set the maximum number of items in the menus.
Chris@376:     void setMenuLimit(int limit);
Chris@376: 
Chris@376:     /// Return the time after which a bundle will be closed if nothing is added.
Chris@376:     int getBundleTimeout() const { return m_bundleTimeout; }
Chris@376: 
Chris@376:     /// Set the time after which a bundle will be closed if nothing is added.
Chris@376:     void setBundleTimeout(int msec);
Chris@376: 
Chris@376:     /// Start recording commands to batch up into a single compound command.
Chris@376:     void startCompoundOperation(QString name, bool execute);
Chris@376: 
Chris@376:     /// Finish recording commands and store the compound command.
Chris@376:     void endCompoundOperation();
Chris@376: 
Chris@376: public slots:
Chris@376:     /**
Chris@376:      * Checkpoint function that should be called when the document is
Chris@376:      * saved.  If the undo/redo stack later returns to the point at
Chris@376:      * which the document was saved, the documentRestored signal will
Chris@376:      * be emitted.
Chris@376:      */
Chris@376:     virtual void documentSaved();
Chris@376: 
Chris@376:     /**
Chris@376:      * Add a command to the history that has already been executed,
Chris@376:      * without executing it again.  Equivalent to addCommand(command, false).
Chris@376:      */
Chris@376:     void addExecutedCommand(Command *);
Chris@376: 
Chris@376:     /**
Chris@376:      * Add a command to the history and also execute it.  Equivalent
Chris@376:      * to addCommand(command, true).
Chris@376:      */
Chris@376:     void addCommandAndExecute(Command *);
Chris@376: 
Chris@376:     void undo();
Chris@376:     void redo();
Chris@376: 
Chris@376: protected slots:
Chris@376:     void undoActivated(QAction *);
Chris@376:     void redoActivated(QAction *);
Chris@376:     void bundleTimerTimeout();
Chris@376:     
Chris@376: signals:
Chris@376:     /**
Chris@376:      * Emitted whenever a command has just been executed or
Chris@376:      * unexecuted, whether by addCommand, undo, or redo.
Chris@376:      */
Chris@376:     void commandExecuted();
Chris@376: 
Chris@376:     /**
Chris@376:      * Emitted whenever a command has just been executed, whether by
Chris@376:      * addCommand or redo.
Chris@376:      */
Chris@376:     void commandExecuted(Command *);
Chris@376: 
Chris@376:     /**
Chris@376:      * Emitted whenever a command has just been unexecuted, whether by
Chris@376:      * addCommand or undo.
Chris@376:      */
Chris@376:     void commandUnexecuted(Command *);
Chris@376: 
Chris@376:     /**
Chris@376:      * Emitted when the undo/redo stack has reached the same state at
Chris@376:      * which the documentSaved slot was last called.
Chris@376:      */
Chris@376:     void documentRestored();
Chris@376: 
Chris@502:     /**
Chris@502:      * Emitted when some activity happened (for activity logging).
Chris@502:      */
Chris@502:     void activity(QString);
Chris@502: 
Chris@376: protected:
Chris@376:     CommandHistory();
Chris@376:     static CommandHistory *m_instance;
Chris@376: 
Chris@376:     QAction *m_undoAction;
Chris@376:     QAction *m_redoAction;
Chris@376:     QAction *m_undoMenuAction;
Chris@376:     QAction *m_redoMenuAction;
Chris@376:     QMenu *m_undoMenu;
Chris@376:     QMenu *m_redoMenu;
Chris@376: 
Chris@376:     std::map<QAction *, int> m_actionCounts;
Chris@376: 
Chris@376:     typedef std::stack<Command *> CommandStack;
Chris@376:     CommandStack m_undoStack;
Chris@376:     CommandStack m_redoStack;
Chris@376: 
Chris@376:     int m_undoLimit;
Chris@376:     int m_redoLimit;
Chris@376:     int m_menuLimit;
Chris@376:     int m_savedAt;
Chris@376: 
Chris@376:     MacroCommand *m_currentCompound;
Chris@376:     bool m_executeCompound;
Chris@376:     void addToCompound(Command *command, bool execute);
Chris@376: 
Chris@376:     MacroCommand *m_currentBundle;
Chris@502:     bool m_bundling;
Chris@376:     QString m_currentBundleName;
Chris@376:     QTimer *m_bundleTimer;
Chris@376:     int m_bundleTimeout;
Chris@376:     void addToBundle(Command *command, bool execute);
Chris@376:     void closeBundle();
Chris@376:     
Chris@376:     void updateActions();
Chris@376: 
Chris@376:     void clipCommands();
Chris@376: 
Chris@376:     void clipStack(CommandStack &stack, int limit);
Chris@376:     void clearStack(CommandStack &stack);
Chris@376: };
Chris@376: 
Chris@376: 
Chris@376: #endif