changeset 47:bac8b14ab355

* Add menu for re-adding existing layers * Fix layer tree window so that it at least approximates correct * Add bundled operations in command history, for use with things like multiple consecutive changes to a parameter value * Disambiguate plugins that happen to have identical descriptions * Add spectral centroid plugin (could use some parameters!) * Some other fixes
author Chris Cannam
date Fri, 17 Mar 2006 17:38:28 +0000
parents 5364a9d338a2
children b1d51d0521c3
files base/Command.cpp base/Command.h base/CommandHistory.cpp base/CommandHistory.h base/Layer.cpp base/Layer.h base/PropertyContainer.cpp plugin/FeatureExtractionPluginFactory.cpp transform/TransformFactory.cpp transform/TransformFactory.h
diffstat 10 files changed, 223 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/base/Command.cpp	Thu Mar 16 18:46:00 2006 +0000
+++ b/base/Command.cpp	Fri Mar 17 17:38:28 2006 +0000
@@ -41,6 +41,12 @@
     }
 }
 
+bool
+MacroCommand::haveCommands() const
+{
+    return !m_commands.empty();
+}
+
 void
 MacroCommand::execute()
 {
@@ -57,3 +63,15 @@
     }
 }
 
+QString
+MacroCommand::getName() const
+{
+    return m_name;
+}
+
+void
+MacroCommand::setName(QString name)
+{
+    m_name = name;
+}
+
--- a/base/Command.h	Thu Mar 16 18:46:00 2006 +0000
+++ b/base/Command.h	Fri Mar 17 17:38:28 2006 +0000
@@ -31,13 +31,13 @@
 
     virtual void addCommand(Command *command);
     virtual void deleteCommand(Command *command);
-    virtual bool haveCommands() const { return !m_commands.empty(); }
+    virtual bool haveCommands() const;
 
     virtual void execute();
     virtual void unexecute();
 
-    virtual QString getName() const { return m_name; }
-    virtual void setName(QString name) { m_name = name; }
+    virtual QString getName() const;
+    virtual void setName(QString name);
 
 protected:
     QString m_name;
--- a/base/CommandHistory.cpp	Thu Mar 16 18:46:00 2006 +0000
+++ b/base/CommandHistory.cpp	Fri Mar 17 17:38:28 2006 +0000
@@ -25,6 +25,7 @@
 #include <QMenu>
 #include <QToolBar>
 #include <QString>
+#include <QTimer>
 
 #include <iostream>
 
@@ -35,8 +36,11 @@
     m_redoLimit(50),
     m_menuLimit(15),
     m_savedAt(0),
-    m_currentMacro(0),
-    m_executeMacro(false)
+    m_currentCompound(0),
+    m_executeCompound(false),
+    m_currentBundle(0),
+    m_bundleTimer(0),
+    m_bundleTimeout(5000)
 {
     m_undoAction = new QAction(QIcon(":/icons/undo.png"), tr("&Undo"), this);
     m_undoAction->setShortcut(tr("Ctrl+Z"));
@@ -83,6 +87,8 @@
 void
 CommandHistory::clear()
 {
+    std::cerr << "CommandHistory::clear()" << std::endl;
+    closeBundle();
     m_savedAt = -1;
     clearStack(m_undoStack);
     clearStack(m_redoStack);
@@ -104,18 +110,26 @@
 }
 
 void
-CommandHistory::addCommand(Command *command, bool execute)
+CommandHistory::addCommand(Command *command, bool execute, bool bundle)
 {
     if (!command) return;
 
-    if (m_currentMacro) {
-	addToMacro(command);
+    if (m_currentCompound) {
+	addToCompound(command);
 	return;
     }
 
-    std::cerr << "MVCH::addCommand: " << command->getName().toLocal8Bit().data() << " at " << command << std::endl;
+    if (bundle) {
+	addToBundle(command, execute);
+	return;
+    } else if (m_currentBundle) {
+	closeBundle();
+    }
+
+    std::cerr << "CommandHistory::addCommand: " << command->getName().toLocal8Bit().data() << " at " << command << std::endl;
 
     // We can't redo after adding a command
+    std::cerr << "CommandHistory::clearing redo stack" << std::endl;
     clearStack(m_redoStack);
 
     // can we reach savedAt?
@@ -137,39 +151,83 @@
 }
 
 void
-CommandHistory::addToMacro(Command *command)
+CommandHistory::addToBundle(Command *command, bool execute)
 {
-    std::cerr << "MVCH::addToMacro: " << command->getName().toLocal8Bit().data() << std::endl;
+    if (m_currentBundle) {
+	if (!command || (command->getName() != m_currentBundleName)) {
+	    closeBundle();
+	}
+    }
 
-    if (m_executeMacro) command->execute();
-    m_currentMacro->addCommand(command);
+    if (!command) return;
+
+    if (!m_currentBundle) {
+	// need to addCommand before setting m_currentBundle, as addCommand
+	// with bundle false will reset m_currentBundle to 0
+	MacroCommand *mc = new MacroCommand(command->getName());
+	addCommand(mc, false);
+	m_currentBundle = mc;
+	m_currentBundleName = command->getName();
+    }
+
+    if (execute) command->execute();
+    m_currentBundle->addCommand(command);
+
+    delete m_bundleTimer;
+    m_bundleTimer = new QTimer(this);
+    connect(m_bundleTimer, SIGNAL(timeout()), this, SLOT(bundleTimerTimeout()));
+    m_bundleTimer->start(m_bundleTimeout);
+}
+
+void
+CommandHistory::closeBundle()
+{
+    m_currentBundle = 0;
+    m_currentBundleName = "";
+}
+
+void
+CommandHistory::bundleTimerTimeout()
+{
+    closeBundle();
+}
+
+void
+CommandHistory::addToCompound(Command *command)
+{
+    std::cerr << "CommandHistory::addToCompound: " << command->getName().toLocal8Bit().data() << std::endl;
+
+    if (m_executeCompound) command->execute();
+    m_currentCompound->addCommand(command);
 }
 
 void
 CommandHistory::startCompoundOperation(QString name, bool execute)
 {
-    if (m_currentMacro) {
-	std::cerr << "MVCH::startCompoundOperation: ERROR: compound operation already in progress!" << std::endl;
-	std::cerr << "(name is " << m_currentMacro->getName().toLocal8Bit().data() << ")" << std::endl;
+    if (m_currentCompound) {
+	std::cerr << "CommandHistory::startCompoundOperation: ERROR: compound operation already in progress!" << std::endl;
+	std::cerr << "(name is " << m_currentCompound->getName().toLocal8Bit().data() << ")" << std::endl;
     }
-    
-    m_currentMacro = new MacroCommand(name);
-    m_executeMacro = execute;
+ 
+    closeBundle();
+   
+    m_currentCompound = new MacroCommand(name);
+    m_executeCompound = execute;
 }
 
 void
 CommandHistory::endCompoundOperation()
 {
-    if (!m_currentMacro) {
-	std::cerr << "MVCH::endCompoundOperation: ERROR: no compound operation in progress!" << std::endl;
+    if (!m_currentCompound) {
+	std::cerr << "CommandHistory::endCompoundOperation: ERROR: no compound operation in progress!" << std::endl;
     }
 
-    Command *toAdd = m_currentMacro;
-    m_currentMacro = 0;
+    Command *toAdd = m_currentCompound;
+    m_currentCompound = 0;
 
     // We don't execute the macro command here, because we have been
     // executing the individual commands as we went along if
-    // m_executeMacro was true.
+    // m_executeCompound was true.
     addCommand(toAdd, false);
 }    
 
@@ -190,6 +248,8 @@
 {
     if (m_undoStack.empty()) return;
 
+    closeBundle();
+
     Command *command = m_undoStack.top();
     command->unexecute();
     emit commandExecuted();
@@ -209,6 +269,8 @@
 {
     if (m_redoStack.empty()) return;
 
+    closeBundle();
+
     Command *command = m_redoStack.top();
     command->execute();
     emit commandExecuted();
@@ -249,8 +311,15 @@
 }
 
 void
+CommandHistory::setBundleTimeout(int ms)
+{
+    m_bundleTimeout = ms;
+}
+
+void
 CommandHistory::documentSaved()
 {
+    closeBundle();
     m_savedAt = m_undoStack.size();
 }
 
@@ -276,7 +345,7 @@
 
 	for (i = 0; i < limit; ++i) {
 	    Command *command = stack.top();
-	    std::cerr << "MVCH::clipStack: Saving recent command: " << command->getName().toLocal8Bit().data() << " at " << command << std::endl;
+	    std::cerr << "CommandHistory::clipStack: Saving recent command: " << command->getName().toLocal8Bit().data() << " at " << command << std::endl;
 	    tempStack.push(stack.top());
 	    stack.pop();
 	}
@@ -296,7 +365,7 @@
     while (!stack.empty()) {
 	Command *command = stack.top();
 	// Not safe to call getName() on a command about to be deleted
-	std::cerr << "MVCH::clearStack: About to delete command " << command << std::endl;
+	std::cerr << "CommandHistory::clearStack: About to delete command " << command << std::endl;
 	delete command;
 	stack.pop();
     }
--- a/base/CommandHistory.h	Thu Mar 16 18:46:00 2006 +0000
+++ b/base/CommandHistory.h	Fri Mar 17 17:38:28 2006 +0000
@@ -32,6 +32,7 @@
 class QAction;
 class QMenu;
 class QToolBar;
+class QTimer;
 
 /**
  * The CommandHistory class stores a list of executed commands and
@@ -57,7 +58,27 @@
     void registerMenu(QMenu *menu);
     void registerToolbar(QToolBar *toolbar);
 
-    void addCommand(Command *command, bool execute = true);
+    /**
+     * Add a command to the command history.
+     *
+     * If execute is true, the command will be executed before being
+     * added.  Otherwise it will be assumed to have been already
+     * executed -- a command should not be added to the history unless
+     * its work has actually been done somehow!
+     *
+     * If a compound operation is in use (see startCompoundOperation
+     * below), the execute status of the compound operation will
+     * override any value of execute passed to this method.
+     *
+     * If bundle is true, the command will be a candidate for bundling
+     * with any adjacent bundeable commands that have the same name,
+     * into a single compound command.  This is useful for small
+     * commands that may be executed repeatedly altering the same data
+     * (e.g. type text, set a parameter) whose number and extent is
+     * not known in advance.  The bundle parameter will be ignored if
+     * a compound operation is already in use.
+     */
+    void addCommand(Command *command, bool execute = true, bool bundle = false);
     
     /// Return the maximum number of items in the undo history.
     int getUndoLimit() const { return m_undoLimit; }
@@ -77,6 +98,12 @@
     /// Set the maximum number of items in the menus.
     void setMenuLimit(int limit);
 
+    /// Return the time after which a bundle will be closed if nothing is added.
+    int getBundleTimeout() const { return m_bundleTimeout; }
+
+    /// Set the time after which a bundle will be closed if nothing is added.
+    void setBundleTimeout(int msec);
+
     /// Start recording commands to batch up into a single compound command.
     void startCompoundOperation(QString name, bool execute);
 
@@ -109,7 +136,8 @@
     void redo();
     void undoActivated(QAction *);
     void redoActivated(QAction *);
-
+    void bundleTimerTimeout();
+    
 signals:
     /**
      * Emitted whenever a command has just been executed or
@@ -157,9 +185,16 @@
     int m_menuLimit;
     int m_savedAt;
 
-    MacroCommand *m_currentMacro;
-    bool m_executeMacro;
-    void addToMacro(Command *command);
+    MacroCommand *m_currentCompound;
+    bool m_executeCompound;
+    void addToCompound(Command *command);
+
+    MacroCommand *m_currentBundle;
+    QString m_currentBundleName;
+    QTimer *m_bundleTimer;
+    int m_bundleTimeout;
+    void addToBundle(Command *command, bool execute);
+    void closeBundle();
     
     void updateActions();
 
--- a/base/Layer.cpp	Thu Mar 16 18:46:00 2006 +0000
+++ b/base/Layer.cpp	Fri Mar 17 17:38:28 2006 +0000
@@ -9,6 +9,7 @@
 
 #include "Layer.h"
 #include "View.h"
+#include "Model.h"
 
 #include <iostream>
 
@@ -31,6 +32,23 @@
 	(LayerFactory::instance()->getLayerType(this));
 }
 
+QString
+Layer::getLayerPresentationName() const
+{
+    QString layerName = objectName();
+    QString modelName;
+    if (getModel()) modelName = getModel()->objectName();
+	    
+    QString text;
+    if (modelName != "") {
+	text = QString("%1: %2").arg(modelName).arg(layerName);
+    } else {
+	text = layerName;
+    }
+	
+    return text;
+}
+
 void
 Layer::setObjectName(const QString &name)
 {
--- a/base/Layer.h	Thu Mar 16 18:46:00 2006 +0000
+++ b/base/Layer.h	Fri Mar 17 17:38:28 2006 +0000
@@ -66,6 +66,8 @@
 	return objectName();
     }
 
+    virtual QString getLayerPresentationName() const;
+
     virtual int getVerticalScaleWidth(View *, QPainter &) const { return 0; }
     virtual void paintVerticalScale(View *, QPainter &, QRect) const { }
 
--- a/base/PropertyContainer.cpp	Thu Mar 16 18:46:00 2006 +0000
+++ b/base/PropertyContainer.cpp	Fri Mar 17 17:38:28 2006 +0000
@@ -57,7 +57,7 @@
     if (value == currentValue) return;
 
     CommandHistory::getInstance()->addCommand
-	(new SetPropertyCommand(this, name, value));
+	(new SetPropertyCommand(this, name, value), true, true); // bundled
 }
 
 PropertyContainer::SetPropertyCommand::SetPropertyCommand(PropertyContainer *pc,
--- a/plugin/FeatureExtractionPluginFactory.cpp	Thu Mar 16 18:46:00 2006 +0000
+++ b/plugin/FeatureExtractionPluginFactory.cpp	Fri Mar 17 17:38:28 2006 +0000
@@ -13,7 +13,7 @@
 #include "plugins/BeatDetect.h" //!!!
 #include "plugins/ChromagramPlugin.h" //!!!
 #include "plugins/ZeroCrossing.h" //!!!
-// NOTE: added by Martin Gasser
+#include "plugins/SpectralCentroid.h" //!!!
 #include "plugins/TonalChangeDetect.h" //!!!
 
 #include <iostream>
@@ -69,8 +69,8 @@
     rv.push_back("sv:_builtin:beats"); //!!!
     rv.push_back("sv:_builtin:chromagram"); //!!!
     rv.push_back("sv:_builtin:zerocrossing"); //!!!
-	// NOTE: added by Martin Gasser
-	rv.push_back("sv:_builtin:tonalchange"); //!!!
+    rv.push_back("sv:_builtin:spectralcentroid"); //!!!
+    rv.push_back("sv:_builtin:tonalchange"); //!!!
     return rv;
 }
 
@@ -103,7 +103,10 @@
 	return new ZeroCrossing(inputSampleRate); //!!!
     }
 
-	// NOTE: added by Martin Gasser
+    if (label == "spectralcentroid") {
+	return new SpectralCentroid(inputSampleRate); //!!!
+    }
+
     if (label == "tonalchange") {
 	return new TonalChangeDetect(inputSampleRate); //!!!
     }
--- a/transform/TransformFactory.cpp	Thu Mar 16 18:46:00 2006 +0000
+++ b/transform/TransformFactory.cpp	Fri Mar 17 17:38:28 2006 +0000
@@ -47,10 +47,11 @@
 void
 TransformFactory::populateTransforms()
 {
-    //!!!
     std::vector<QString> fexplugs =
 	FeatureExtractionPluginFactory::getAllPluginIdentifiers();
 
+    std::map<QString, QString> makers;
+
     for (size_t i = 0; i < fexplugs.size(); ++i) {
 
 	QString pluginId = fexplugs[i];
@@ -63,8 +64,6 @@
 	    continue;
 	}
 
-	//!!! well, really we want to be able to query this without having to instantiate
-
 	FeatureExtractionPlugin *plugin = 
 	    factory->instantiatePlugin(pluginId, 48000);
 
@@ -93,8 +92,44 @@
 	    }
 
 	    m_transforms[transformName] = userDescription;
+	    
+	    makers[transformName] = plugin->getMaker().c_str();
 	}
     }
+
+    // disambiguate plugins with similar descriptions
+
+    std::map<QString, int> descriptions;
+
+    for (TransformMap::iterator i = m_transforms.begin(); i != m_transforms.end();
+	 ++i) {
+
+	QString name = i->first, description = i->second;
+
+	++descriptions[description];
+	++descriptions[QString("%1 [%2]").arg(description).arg(makers[name])];
+    }
+
+    std::map<QString, int> counts;
+    TransformMap newMap;
+
+    for (TransformMap::iterator i = m_transforms.begin(); i != m_transforms.end();
+	 ++i) {
+
+	QString name = i->first, description = i->second;
+
+	if (descriptions[description] > 1) {
+	    description = QString("%1 [%2]").arg(description).arg(makers[name]);
+	    if (descriptions[description] > 1) {
+		description = QString("%1 <%2>")
+		    .arg(description).arg(++counts[description]);
+	    }
+	}
+
+	newMap[name] = description;
+    }	    
+	    
+    m_transforms = newMap;
 }
 
 QString
--- a/transform/TransformFactory.h	Thu Mar 16 18:46:00 2006 +0000
+++ b/transform/TransformFactory.h	Fri Mar 17 17:38:28 2006 +0000
@@ -24,8 +24,10 @@
     static TransformFactory *instance();
 
     // The name is intended to be computer-referencable, and unique
-    // within the application.  The description should be
-    // human-readable, and does not have to be unique.
+    // within the application.  The description is intended to be
+    // human readable.  In principle it doesn't have to be unique, but
+    // the factory will add suffixes to ensure that it is, all the
+    // same (just to avoid user confusion).
 
     struct TransformDesc {
 	TransformDesc(TransformName _name, QString _description = "") :