diff main/MainWindow.cpp @ 34:8ad306d8a568

* 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
author Chris Cannam
date Fri, 22 Sep 2006 16:12:23 +0000
parents 544ab25d2372
children da7a3828727f
line wrap: on
line diff
--- 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<QString, QMenu *> byPluginNameMenus;
     map<QString, map<QString, QMenu *> > 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<QString>::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<QString> 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<QString> 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<QString, set<QString> > pluginNameLists;
+    map<QString, map<QString, QMenu *> > 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<QString>::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<QString>::iterator j = pluginNameLists[*i].begin();
+             j != pluginNameLists[*i].end();
+             ++j) {
+
+            pluginNameToChunkMenuMap[*i][*j] = chunkMenu;
+
+            set<QString>::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<QString> files = RecentFiles::getInstance()->getRecentFiles();
+    vector<QString> 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<QString> 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);
 }