# HG changeset patch # User Chris Cannam # Date 1158941543 0 # Node ID 8ad306d8a568183df7eaed4efc0281cd56c2fc1c # Parent 544ab25d2372e26f7e1d2ce88801cdf640675081 * Pull transforms out of Layer menu (again) and into a separate Transforms menu * Add Recent Transforms submenu * Add effects and generators to the transforms menu (not yet implemented) as well as analysis plugins and data-from-effects (control output ports) * Add a nice dictionary-volume-style alphabetic subdivision of plugin names in plugins By Name menus diff -r 544ab25d2372 -r 8ad306d8a568 main/MainWindow.cpp --- a/main/MainWindow.cpp Thu Sep 21 16:43:50 2006 +0000 +++ b/main/MainWindow.cpp Fri Sep 22 16:12:23 2006 +0000 @@ -100,12 +100,18 @@ m_timeRulerLayer(0), m_playSource(0), m_playTarget(0), + m_recentFiles("RecentFiles"), + m_recentTransforms("RecentTransforms"), m_mainMenusCreated(false), m_paneMenu(0), m_layerMenu(0), + m_transformsMenu(0), m_existingLayersMenu(0), + m_recentFilesMenu(0), + m_recentTransformsMenu(0), m_rightButtonMenu(0), m_rightButtonLayerMenu(0), + m_rightButtonTransformsMenu(0), m_documentModified(false), m_preferencesDialog(0) { @@ -250,6 +256,13 @@ m_rightButtonMenu->addSeparator(); } + if (m_rightButtonTransformsMenu) { + m_rightButtonTransformsMenu->clear(); + } else { + m_rightButtonTransformsMenu = m_rightButtonMenu->addMenu(tr("&Transform")); + m_rightButtonMenu->addSeparator(); + } + if (!m_mainMenusCreated) { CommandHistory::getInstance()->registerMenu(m_rightButtonMenu); @@ -337,9 +350,8 @@ menu->addSeparator(); m_recentFilesMenu = menu->addMenu(tr("&Recent Files")); - menu->addMenu(m_recentFilesMenu); setupRecentFilesMenu(); - connect(RecentFiles::getInstance(), SIGNAL(recentFilesChanged()), + connect(&m_recentFiles, SIGNAL(recentChanged()), this, SLOT(setupRecentFilesMenu())); menu->addSeparator(); @@ -565,13 +577,20 @@ } if (m_layerMenu) { - m_layerTransformActions.clear(); m_layerActions.clear(); m_layerMenu->clear(); } else { m_layerMenu = menuBar()->addMenu(tr("&Layer")); } + if (m_transformsMenu) { + m_transformActions.clear(); + m_transformActionsReverse.clear(); + m_transformsMenu->clear(); + } else { + m_transformsMenu = menuBar()->addMenu(tr("&Transform")); + } + TransformFactory::TransformList transforms = TransformFactory::getInstance()->getAllTransforms(); @@ -584,16 +603,24 @@ map byPluginNameMenus; map > pluginNameMenus; + m_recentTransformsMenu = m_transformsMenu->addMenu(tr("&Recent Transforms")); + m_rightButtonTransformsMenu->addMenu(m_recentTransformsMenu); + connect(&m_recentTransforms, SIGNAL(recentChanged()), + this, SLOT(setupRecentTransformsMenu())); + + m_transformsMenu->addSeparator(); + m_rightButtonTransformsMenu->addSeparator(); + for (vector::iterator i = types.begin(); i != types.end(); ++i) { if (i != types.begin()) { - m_layerMenu->addSeparator(); - m_rightButtonLayerMenu->addSeparator(); + m_transformsMenu->addSeparator(); + m_rightButtonTransformsMenu->addSeparator(); } QString byCategoryLabel = tr("%1 by Category").arg(*i); - QMenu *byCategoryMenu = m_layerMenu->addMenu(byCategoryLabel); - m_rightButtonLayerMenu->addMenu(byCategoryMenu); + QMenu *byCategoryMenu = m_transformsMenu->addMenu(byCategoryLabel); + m_rightButtonTransformsMenu->addMenu(byCategoryMenu); vector categories = TransformFactory::getInstance()->getTransformCategories(*i); @@ -630,9 +657,13 @@ } } + QString byPluginNameLabel = tr("%1 by Plugin Name").arg(*i); + byPluginNameMenus[*i] = m_transformsMenu->addMenu(byPluginNameLabel); + m_rightButtonTransformsMenu->addMenu(byPluginNameMenus[*i]); + QString byMakerLabel = tr("%1 by Maker").arg(*i); - QMenu *byMakerMenu = m_layerMenu->addMenu(byMakerLabel); - m_rightButtonLayerMenu->addMenu(byMakerMenu); + QMenu *byMakerMenu = m_transformsMenu->addMenu(byMakerLabel); + m_rightButtonTransformsMenu->addMenu(byMakerMenu); vector makers = TransformFactory::getInstance()->getTransformMakers(*i); @@ -645,12 +676,95 @@ makerMenus[*i][maker] = byMakerMenu->addMenu(maker); } - - QString byPluginNameLabel = tr("%1 by Plugin Name").arg(*i); - byPluginNameMenus[*i] = m_layerMenu->addMenu(byPluginNameLabel); - m_rightButtonLayerMenu->addMenu(byPluginNameMenus[*i]); } + map > pluginNameLists; + map > pluginNameToChunkMenuMap; + + for (unsigned int i = 0; i < transforms.size(); ++i) { + QString description = transforms[i].description; + if (description == "") description = transforms[i].name; + QString type = transforms[i].type; + QString pluginName = description.section(": ", 0, 0); + pluginNameLists[type].insert(pluginName); + } + + for (vector::iterator i = types.begin(); i != types.end(); ++i) { + + size_t total = pluginNameLists[*i].size(); + size_t chunk = 14; + + if (total < (chunk * 3) / 2) continue; + + size_t count = 0; + QMenu *chunkMenu = new QMenu(); + + QString firstNameInChunk; + QChar firstInitialInChunk; + bool discriminateStartInitial = false; + + for (set::iterator j = pluginNameLists[*i].begin(); + j != pluginNameLists[*i].end(); + ++j) { + + pluginNameToChunkMenuMap[*i][*j] = chunkMenu; + + set::iterator k = j; + ++k; + + QChar initial = (*j)[0]; + + if (count == 0) { + firstNameInChunk = *j; + firstInitialInChunk = initial; + } + + bool lastInChunk = (k == pluginNameLists[*i].end() || + (count >= chunk-1 && + (count == (5*chunk) / 2 || + (*k)[0] != initial))); + + ++count; + + if (lastInChunk) { + + bool discriminateEndInitial = (k != pluginNameLists[*i].end() && + (*k)[0] == initial); + + bool initialsEqual = (firstInitialInChunk == initial); + + QString from = QString("%1").arg(firstInitialInChunk); + if (discriminateStartInitial || + (discriminateEndInitial && initialsEqual)) { + from = firstNameInChunk.left(3); + } + + QString to = QString("%1").arg(initial); + if (discriminateEndInitial || + (discriminateStartInitial && initialsEqual)) { + to = j->left(3); + } + + QString menuText; + + if (from == to) menuText = from; + else menuText = tr("%1 - %2").arg(from).arg(to); + + discriminateStartInitial = discriminateEndInitial; + + chunkMenu->setTitle(menuText); + + byPluginNameMenus[*i]->addMenu(chunkMenu); + + chunkMenu = new QMenu(); + + count = 0; + } + } + + if (count == 0) delete chunkMenu; + } + for (unsigned int i = 0; i < transforms.size(); ++i) { QString description = transforms[i].description; @@ -669,7 +783,8 @@ action = new QAction(tr("%1...").arg(description), this); connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); - m_layerTransformActions[action] = transforms[i].name; + m_transformActions[action] = transforms[i].name; + m_transformActionsReverse[transforms[i].name] = action; connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); if (categoryMenus[type].find(category) == categoryMenus[type].end()) { @@ -692,17 +807,22 @@ action = new QAction(tr("%1...").arg(output == "" ? pluginName : output), this); connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); - m_layerTransformActions[action] = transforms[i].name; + m_transformActions[action] = transforms[i].name; connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); +// cerr << "Transform: \"" << name.toStdString() << "\": plugin name \"" << pluginName.toStdString() << "\"" << endl; + if (pluginNameMenus[type].find(pluginName) == pluginNameMenus[type].end()) { + QMenu *parentMenu = pluginNameToChunkMenuMap[type][pluginName]; + if (!parentMenu) parentMenu = byPluginNameMenus[type]; + if (output == "") { - byPluginNameMenus[type]->addAction(action); + parentMenu->addAction(action); } else { - pluginNameMenus[type][pluginName] = - byPluginNameMenus[type]->addMenu(pluginName); + pluginNameMenus[type][pluginName] = + parentMenu->addMenu(pluginName); connect(this, SIGNAL(canAddLayer(bool)), pluginNameMenus[type][pluginName], SLOT(setEnabled(bool))); @@ -715,7 +835,7 @@ } } - m_rightButtonLayerMenu->addSeparator(); + setupRecentTransformsMenu(); menu = m_paneMenu; @@ -731,7 +851,7 @@ menu = m_layerMenu; - menu->addSeparator(); +// menu->addSeparator(); LayerFactory::LayerTypeSet emptyLayerTypes = LayerFactory::getInstance()->getValidEmptyLayerTypes(); @@ -988,7 +1108,7 @@ MainWindow::setupRecentFilesMenu() { m_recentFilesMenu->clear(); - vector files = RecentFiles::getInstance()->getRecentFiles(); + vector files = m_recentFiles.getRecent(); for (size_t i = 0; i < files.size(); ++i) { QAction *action = new QAction(files[i], this); connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile())); @@ -997,6 +1117,24 @@ } void +MainWindow::setupRecentTransformsMenu() +{ + m_recentTransformsMenu->clear(); + vector transforms = m_recentTransforms.getRecent(); + for (size_t i = 0; i < transforms.size(); ++i) { + TransformActionReverseMap::iterator ti = + m_transformActionsReverse.find(transforms[i]); + if (ti == m_transformActionsReverse.end()) { + std::cerr << "WARNING: MainWindow::setupRecentTransformsMenu: " + << "Unknown transform \"" << transforms[i].toStdString() + << "\" in recent transforms list" << std::endl; + continue; + } + m_recentTransformsMenu->addAction(ti->second); + } +} + +void MainWindow::setupExistingLayersMenu() { if (!m_existingLayersMenu) return; // should have been created by setupMenus @@ -1702,7 +1840,7 @@ if (ok) { if (!multiple) { - RecentFiles::getInstance()->addFile(path); + m_recentFiles.addFile(path); } } else { QMessageBox::critical(this, tr("Failed to write file"), error); @@ -1783,7 +1921,7 @@ return false; } - RecentFiles::getInstance()->addFile(path); + m_recentFiles.addFile(path); return true; } else { @@ -1794,7 +1932,7 @@ Layer *newLayer = m_document->createImportedLayer(model); if (newLayer) { m_document->addLayerToView(pane, newLayer); - RecentFiles::getInstance()->addFile(path); + m_recentFiles.addFile(path); return true; } } @@ -1862,7 +2000,7 @@ if (error != "") { QMessageBox::critical(this, tr("Failed to write file"), error); } else { - RecentFiles::getInstance()->addFile(path); + m_recentFiles.addFile(path); } } @@ -1970,7 +2108,7 @@ } updateMenuStates(); - RecentFiles::getInstance()->addFile(path); + m_recentFiles.addFile(path); return true; } @@ -2275,7 +2413,7 @@ CommandHistory::getInstance()->documentSaved(); m_documentModified = false; updateMenuStates(); - RecentFiles::getInstance()->addFile(path); + m_recentFiles.addFile(path); } else { setWindowTitle(tr("Sonic Visualiser")); } @@ -2329,7 +2467,7 @@ .arg(QProcess().pid()); QString fpath = QDir(svDir).filePath(fname); if (saveSessionFile(fpath)) { - RecentFiles::getInstance()->addFile(fpath); + m_recentFiles.addFile(fpath); return true; } else { return false; @@ -2438,7 +2576,7 @@ m_sessionFile = path; CommandHistory::getInstance()->documentSaved(); documentRestored(); - RecentFiles::getInstance()->addFile(path); + m_recentFiles.addFile(path); } } @@ -2855,9 +2993,9 @@ return; } - TransformActionMap::iterator i = m_layerTransformActions.find(action); - - if (i == m_layerTransformActions.end()) { + TransformActionMap::iterator i = m_transformActions.find(action); + + if (i == m_transformActions.end()) { LayerActionMap::iterator i = m_layerActions.find(action); @@ -2924,6 +3062,7 @@ if (newLayer) { m_document->addLayerToView(pane, newLayer); m_document->setChannel(newLayer, context.channel); + m_recentTransforms.add(transform); } updateMenuStates(); @@ -3170,7 +3309,7 @@ QMessageBox::warning (this, tr("Failed to generate layer"), - tr("The layer transform \"%1\" failed to run.\nThis probably means that a plugin failed to initialise.") + tr("Failed to generate a derived layer.\n\nThe layer transform \"%1\" failed.\n\nThis probably means that a plugin failed to initialise, perhaps because it\nrejected the processing block size that was requested.") .arg(transformName), QMessageBox::Ok, 0); } @@ -3181,7 +3320,7 @@ QMessageBox::warning (this, tr("Failed to regenerate layer"), - tr("Failed to regenerate derived layer \"%1\".\nThe layer transform \"%2\" failed to run.\nThis probably means the layer used a plugin that is not currently available.") + tr("Failed to regenerate derived layer \"%1\".\n\nThe layer transform \"%2\" failed to run.\n\nThis probably means the layer used a plugin that is not currently available.") .arg(layerName).arg(transformName), QMessageBox::Ok, 0); } diff -r 544ab25d2372 -r 8ad306d8a568 main/MainWindow.h --- a/main/MainWindow.h Thu Sep 21 16:43:50 2006 +0000 +++ b/main/MainWindow.h Fri Sep 22 16:12:23 2006 +0000 @@ -24,6 +24,7 @@ #include "base/Command.h" #include "view/ViewManager.h" #include "base/PropertyContainer.h" +#include "base/RecentFiles.h" #include "layer/LayerFactory.h" #include "transform/Transform.h" #include "document/SVFileReader.h" @@ -189,6 +190,7 @@ void preferenceChanged(PropertyContainer::PropertyName); void setupRecentFilesMenu(); + void setupRecentTransformsMenu(); void showLayerTree(); @@ -215,13 +217,19 @@ AudioCallbackPlaySource *m_playSource; AudioCallbackPlayTarget *m_playTarget; + RecentFiles m_recentFiles; + RecentFiles m_recentTransforms; + bool m_mainMenusCreated; QMenu *m_paneMenu; QMenu *m_layerMenu; + QMenu *m_transformsMenu; QMenu *m_existingLayersMenu; QMenu *m_recentFilesMenu; + QMenu *m_recentTransformsMenu; QMenu *m_rightButtonMenu; QMenu *m_rightButtonLayerMenu; + QMenu *m_rightButtonTransformsMenu; bool m_documentModified; @@ -243,7 +251,10 @@ PaneActionMap m_paneActions; typedef std::map TransformActionMap; - TransformActionMap m_layerTransformActions; + TransformActionMap m_transformActions; + + typedef std::map TransformActionReverseMap; + TransformActionReverseMap m_transformActionsReverse; typedef std::map LayerActionMap; LayerActionMap m_layerActions; diff -r 544ab25d2372 -r 8ad306d8a568 transform/RealTimePluginTransform.cpp --- a/transform/RealTimePluginTransform.cpp Thu Sep 21 16:43:50 2006 +0000 +++ b/transform/RealTimePluginTransform.cpp Fri Sep 22 16:12:23 2006 +0000 @@ -66,17 +66,24 @@ PluginXml(m_plugin).setParametersFromXml(configurationXml); } - if (m_outputNo >= m_plugin->getControlOutputCount()) { + if (m_outputNo >= 0 && m_outputNo >= m_plugin->getControlOutputCount()) { std::cerr << "RealTimePluginTransform: Plugin has fewer than desired " << m_outputNo << " control outputs" << std::endl; return; } + + if (m_outputNo == -1) { + + //!!! process audio! + + } else { - SparseTimeValueModel *model = new SparseTimeValueModel - (input->getSampleRate(), m_context.blockSize, 0.0, 0.0, false); + SparseTimeValueModel *model = new SparseTimeValueModel + (input->getSampleRate(), m_context.blockSize, 0.0, 0.0, false); - if (units != "") model->setScaleUnits(units); + if (units != "") model->setScaleUnits(units); - m_output = model; + m_output = model; + } } RealTimePluginTransform::~RealTimePluginTransform() diff -r 544ab25d2372 -r 8ad306d8a568 transform/RealTimePluginTransform.h --- a/transform/RealTimePluginTransform.h Thu Sep 21 16:43:50 2006 +0000 +++ b/transform/RealTimePluginTransform.h Fri Sep 22 16:12:23 2006 +0000 @@ -29,7 +29,7 @@ const ExecutionContext &context, QString configurationXml = "", QString units = "", - int output = 0); + int output = -1); // -1 -> audio, 0+ -> data virtual ~RealTimePluginTransform(); protected: diff -r 544ab25d2372 -r 8ad306d8a568 transform/TransformFactory.cpp --- a/transform/TransformFactory.cpp Thu Sep 21 16:43:50 2006 +0000 +++ b/transform/TransformFactory.cpp Fri Sep 22 16:12:23 2006 +0000 @@ -51,10 +51,16 @@ { if (m_transforms.empty()) populateTransforms(); - TransformList list; + std::set dset; for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); i != m_transforms.end(); ++i) { - list.push_back(i->second); + dset.insert(i->second); + } + + TransformList list; + for (std::set::const_iterator i = dset.begin(); + i != dset.end(); ++i) { + list.push_back(*i); } return list; @@ -233,7 +239,7 @@ !plugin->getParameterDescriptors().empty()); transforms[transformName] = - TransformDesc(tr("Analysis Plugins"), + TransformDesc(tr("Analysis"), category, transformName, userDescription, @@ -251,7 +257,7 @@ std::vector plugs = RealTimePluginFactory::getAllPluginIdentifiers(); - QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$"); + static QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$"); for (size_t i = 0; i < plugs.size(); ++i) { @@ -273,57 +279,81 @@ continue; } - if (descriptor->controlOutputPortCount == 0 || - descriptor->audioInputPortCount == 0) continue; +//!!! if (descriptor->controlOutputPortCount == 0 || +// descriptor->audioInputPortCount == 0) continue; -// std::cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " output ports" << std::endl; + std::cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " control output ports, " << descriptor->audioOutputPortCount << " audio outputs, " << descriptor->audioInputPortCount << " audio inputs" << std::endl; QString pluginDescription = descriptor->name.c_str(); QString category = factory->getPluginCategory(pluginId); + bool configurable = (descriptor->parameterCount > 0); - for (size_t j = 0; j < descriptor->controlOutputPortCount; ++j) { + if (descriptor->audioInputPortCount > 0) { - QString transformName = QString("%1:%2").arg(pluginId).arg(j); - QString userDescription; - QString units; + for (size_t j = 0; j < descriptor->controlOutputPortCount; ++j) { - if (j < descriptor->controlOutputPortNames.size() && - descriptor->controlOutputPortNames[j] != "") { + QString transformName = QString("%1:%2").arg(pluginId).arg(j); + QString userDescription; + QString units; - QString portName = descriptor->controlOutputPortNames[j].c_str(); + if (j < descriptor->controlOutputPortNames.size() && + descriptor->controlOutputPortNames[j] != "") { - userDescription = tr("%1: %2") - .arg(pluginDescription) - .arg(portName); + QString portName = descriptor->controlOutputPortNames[j].c_str(); - if (unitRE.indexIn(portName) >= 0) { - units = unitRE.cap(1); + userDescription = tr("%1: %2") + .arg(pluginDescription) + .arg(portName); + + if (unitRE.indexIn(portName) >= 0) { + units = unitRE.cap(1); + } + + } else if (descriptor->controlOutputPortCount > 1) { + + userDescription = tr("%1: Output %2") + .arg(pluginDescription) + .arg(j + 1); + + } else { + + userDescription = pluginDescription; } - } else if (descriptor->controlOutputPortCount > 1) { - userDescription = tr("%1: Output %2") - .arg(pluginDescription) - .arg(j + 1); + transforms[transformName] = + TransformDesc(tr("Effects Measurements"), + category, + transformName, + userDescription, + userDescription, + descriptor->maker.c_str(), + units, + configurable); + } + } - } else { + if (!descriptor->isSynth || descriptor->audioInputPortCount > 0) { - userDescription = pluginDescription; + if (descriptor->audioOutputPortCount > 0) { + + QString transformName = QString("%1:A").arg(pluginId); + QString type = tr("Effects"); + if (descriptor->audioInputPortCount == 0) { + type = tr("Generators"); + } + + transforms[transformName] = + TransformDesc(type, + category, + transformName, + pluginDescription, + pluginDescription, + descriptor->maker.c_str(), + "", + configurable); } - - - bool configurable = (descriptor->parameterCount > 0); - - transforms[transformName] = - TransformDesc(tr("Other Plugins"), - category, - transformName, - userDescription, - userDescription, - descriptor->maker.c_str(), - units, - configurable); - } + } } } @@ -516,6 +546,7 @@ context, configurationXml, getTransformUnits(name), + output == "A" ? -1 : output.toInt()); } else { std::cerr << "TransformFactory::createTransform: Unknown transform \"" diff -r 544ab25d2372 -r 8ad306d8a568 transform/TransformFactory.h --- a/transform/TransformFactory.h Thu Sep 21 16:43:50 2006 +0000 +++ b/transform/TransformFactory.h Fri Sep 22 16:12:23 2006 +0000 @@ -41,6 +41,7 @@ // to be user-readable, for use in menus. struct TransformDesc { + TransformDesc() { } TransformDesc(QString _type, QString _category, TransformName _name, QString _description, @@ -50,6 +51,7 @@ name(_name), description(_description), friendlyName(_friendlyName), maker(_maker), units(_units), configurable(_configurable) { } + QString type; QString category; TransformName name; @@ -58,6 +60,10 @@ QString maker; QString units; bool configurable; + + bool operator<(const TransformDesc &od) const { + return (description < od.description); + }; }; typedef std::vector TransformList; @@ -119,7 +125,7 @@ * If the transform has a prescribed number or range of channel * inputs, return true and set minChannels and maxChannels to the * minimum and maximum number of channel inputs the transform can - * accept. + * accept. Return false if it doesn't care. */ bool getTransformChannelRange(TransformName name, int &minChannels, int &maxChannels);