#include "MainWindow.hpp" #include #include #include #include #include #include #include #include "amuse/ContainerRegistry.hpp" #include "Common.hpp" #include "SongGroupEditor.hpp" #include "SoundGroupEditor.hpp" #include "SoundGroupEditor.hpp" #include "SoundMacroEditor.hpp" #include "ADSREditor.hpp" #include "CurveEditor.hpp" #include "KeymapEditor.hpp" #include "LayersEditor.hpp" #include "SampleEditor.hpp" MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), m_treeDelegate(*this, this), m_mainMessenger(this), m_undoStack(new QUndoStack(this)), m_backgroundThread(this) { m_backgroundThread.start(); m_ui.setupUi(this); m_ui.splitter->setCollapsible(1, false); m_ui.projectOutline->setItemDelegate(&m_treeDelegate); connectMessenger(&m_mainMessenger, Qt::DirectConnection); m_ui.statusbar->connectKillClicked(this, SLOT(killSounds())); m_ui.keyboardContents->setStatusFocus(new StatusBarFocus(m_ui.statusbar)); m_ui.velocitySlider->setStatusFocus(new StatusBarFocus(m_ui.statusbar)); m_ui.modulationSlider->setStatusFocus(new StatusBarFocus(m_ui.statusbar)); m_ui.pitchSlider->setStatusFocus(new StatusBarFocus(m_ui.statusbar)); connect(m_ui.keyboardContents, SIGNAL(notePressed(int)), this, SLOT(notePressed(int))); connect(m_ui.keyboardContents, SIGNAL(noteReleased()), this, SLOT(noteReleased())); connect(m_ui.velocitySlider, SIGNAL(valueChanged(int)), this, SLOT(velocityChanged(int))); connect(m_ui.modulationSlider, SIGNAL(valueChanged(int)), this, SLOT(modulationChanged(int))); connect(m_ui.pitchSlider, SIGNAL(valueChanged(int)), this, SLOT(pitchChanged(int))); m_ui.actionNew_Project->setShortcut(QKeySequence::New); connect(m_ui.actionNew_Project, SIGNAL(triggered()), this, SLOT(newAction())); m_ui.actionOpen_Project->setShortcut(QKeySequence::Open); connect(m_ui.actionOpen_Project, SIGNAL(triggered()), this, SLOT(openAction())); m_ui.actionSave_Project->setShortcut(QKeySequence::Save); connect(m_ui.actionSave_Project, SIGNAL(triggered()), this, SLOT(saveAction())); connect(m_ui.actionRevert_Project, SIGNAL(triggered()), this, SLOT(revertAction())); connect(m_ui.actionReload_Sample_Data, SIGNAL(triggered()), this, SLOT(reloadSampleDataAction())); connect(m_ui.actionImport_Groups, SIGNAL(triggered()), this, SLOT(importAction())); connect(m_ui.actionExport_GameCube_Groups, SIGNAL(triggered()), this, SLOT(exportAction())); for (int i = 0; i < MaxRecentFiles; ++i) { m_recentFileActs[i] = new QAction(this); m_recentFileActs[i]->setVisible(false); m_ui.menuRecent_Projects->addAction(m_recentFileActs[i]); connect(m_recentFileActs[i], SIGNAL(triggered()), this, SLOT(openRecentFileAction())); } m_ui.menuRecent_Projects->addSeparator(); m_clearRecentFileAct = new QAction(tr("Clear Recent Projects"), this); connect(m_clearRecentFileAct, SIGNAL(triggered()), this, SLOT(clearRecentFilesAction())); m_ui.menuRecent_Projects->addAction(m_clearRecentFileAct); #ifndef __APPLE__ m_ui.menuFile->addSeparator(); QAction* quitAction = m_ui.menuFile->addAction(tr("Quit")); quitAction->setShortcut(QKeySequence::Quit); connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); #endif updateRecentFileActions(); QAction* undoAction = m_undoStack->createUndoAction(this); undoAction->setShortcut(QKeySequence::Undo); m_ui.menuEdit->insertAction(m_ui.actionCut, undoAction); QAction* redoAction = m_undoStack->createRedoAction(this); redoAction->setShortcut(QKeySequence::Redo); m_ui.menuEdit->insertAction(m_ui.actionCut, redoAction); m_ui.menuEdit->insertSeparator(m_ui.actionCut); m_ui.actionCut->setShortcut(QKeySequence::Cut); m_ui.actionCopy->setShortcut(QKeySequence::Copy); m_ui.actionPaste->setShortcut(QKeySequence::Paste); m_ui.actionDelete->setShortcut(QKeySequence::Delete); onFocusChanged(nullptr, this); QGridLayout* faceLayout = new QGridLayout; QSvgWidget* faceSvg = new QSvgWidget(QStringLiteral(":/bg/FaceGrey.svg")); faceSvg->setGeometry(0, 0, 256, 256); faceSvg->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); faceLayout->addWidget(faceSvg); m_faceSvg = new QWidget; m_faceSvg->setLayout(faceLayout); m_ui.editorContents->addWidget(m_faceSvg); m_songGroupEditor = new SongGroupEditor; m_ui.editorContents->addWidget(m_songGroupEditor); m_soundGroupEditor = new SoundGroupEditor; m_ui.editorContents->addWidget(m_soundGroupEditor); m_soundMacroEditor = new SoundMacroEditor; m_ui.editorContents->addWidget(m_soundMacroEditor); m_adsrEditor = new ADSREditor; m_ui.editorContents->addWidget(m_adsrEditor); m_curveEditor = new CurveEditor; m_ui.editorContents->addWidget(m_curveEditor); m_keymapEditor = new KeymapEditor; m_ui.editorContents->addWidget(m_keymapEditor); m_layersEditor = new LayersEditor; m_ui.editorContents->addWidget(m_layersEditor); m_sampleEditor = new SampleEditor; m_ui.editorContents->addWidget(m_sampleEditor); m_ui.editorContents->setCurrentWidget(m_faceSvg); connect(m_ui.actionNew_Subproject, SIGNAL(triggered()), this, SLOT(newSubprojectAction())); connect(m_ui.actionNew_SFX_Group, SIGNAL(triggered()), this, SLOT(newSFXGroupAction())); connect(m_ui.actionNew_Song_Group, SIGNAL(triggered()), this, SLOT(newSongGroupAction())); connect(m_ui.actionNew_Sound_Macro, SIGNAL(triggered()), this, SLOT(newSoundMacroAction())); connect(m_ui.actionNew_ADSR, SIGNAL(triggered()), this, SLOT(newADSRAction())); connect(m_ui.actionNew_Curve, SIGNAL(triggered()), this, SLOT(newCurveAction())); connect(m_ui.actionNew_Keymap, SIGNAL(triggered()), this, SLOT(newKeymapAction())); connect(m_ui.actionNew_Layers, SIGNAL(triggered()), this, SLOT(newLayersAction())); connect(m_ui.menuAudio, SIGNAL(aboutToShow()), this, SLOT(aboutToShowAudioIOMenu())); connect(m_ui.menuMIDI, SIGNAL(aboutToShow()), this, SLOT(aboutToShowMIDIIOMenu())); connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)), this, SLOT(onFocusChanged(QWidget*,QWidget*))); m_voxEngine = boo::NewAudioVoiceEngine(); m_voxAllocator = std::make_unique(*m_voxEngine); m_engine = std::make_unique(*m_voxAllocator); m_ctrlVals[7] = 127; m_ctrlVals[10] = 64; startTimer(16); } MainWindow::~MainWindow() { m_backgroundThread.quit(); m_backgroundThread.wait(); printf("IM DYING\n"); } void MainWindow::connectMessenger(UIMessenger* messenger, Qt::ConnectionType type) { connect(messenger, SIGNAL(information(const QString&, const QString&, QMessageBox::StandardButtons, QMessageBox::StandardButton)), this, SLOT(msgInformation(const QString&, const QString&, QMessageBox::StandardButtons, QMessageBox::StandardButton)), type); connect(messenger, SIGNAL(question(const QString&, const QString&, QMessageBox::StandardButtons, QMessageBox::StandardButton)), this, SLOT(msgQuestion(const QString&, const QString&, QMessageBox::StandardButtons, QMessageBox::StandardButton)), type); connect(messenger, SIGNAL(warning(const QString&, const QString&, QMessageBox::StandardButtons, QMessageBox::StandardButton)), this, SLOT(msgWarning(const QString&, const QString&, QMessageBox::StandardButtons, QMessageBox::StandardButton)), type); connect(messenger, SIGNAL(critical(const QString&, const QString&, QMessageBox::StandardButtons, QMessageBox::StandardButton)), this, SLOT(msgCritical(const QString&, const QString&, QMessageBox::StandardButtons, QMessageBox::StandardButton)), type); } void MainWindow::updateWindowTitle() { if (!m_projectModel) { setWindowTitle(tr("Amuse")); return; } QDir dir(m_projectModel->path()); if (m_ui.editorContents->currentWidget() != m_faceSvg) { ProjectModel::BasePoolObjectNode* objNode = static_cast( static_cast(m_ui.editorContents->currentWidget())->currentNode()); setWindowTitle(tr("Amuse [%1/%2/%3]").arg(dir.dirName()).arg( m_projectModel->getGroupNode(objNode)->text()).arg(objNode->text())); return; } setWindowTitle(tr("Amuse [%1]").arg(dir.dirName())); } void MainWindow::updateRecentFileActions() { QSettings settings; QStringList files = settings.value("recentFileList").toStringList(); int numRecentFiles = std::min(files.size(), int(MaxRecentFiles)); for (int i = 0; i < numRecentFiles; ++i) { QString text = QStringLiteral("&%1 %2").arg(i + 1).arg(QDir(files[i]).dirName()); m_recentFileActs[i]->setText(text); m_recentFileActs[i]->setData(files[i]); m_recentFileActs[i]->setVisible(true); } for (int j = numRecentFiles; j < MaxRecentFiles; ++j) m_recentFileActs[j]->setVisible(false); m_ui.menuRecent_Projects->setEnabled(numRecentFiles > 0); } bool MainWindow::setProjectPath(const QString& path) { if (m_projectModel && m_projectModel->path() == path) return true; QDir dir(path); if (dir.path().isEmpty() || dir.path() == QStringLiteral(".") || dir.path() == QStringLiteral("..")) { QString msg = QString(tr("The directory at '%1' must not be empty.")).arg(path); QMessageBox::critical(this, tr("Directory empty"), msg); return false; } else if (!dir.exists()) { QString msg = QString(tr("The directory at '%1' must exist for the Amuse editor.")).arg(path); QMessageBox::critical(this, tr("Directory does not exist"), msg); return false; } QString testWritePath = dir.filePath(tr("test")); QFile testWriteFile(testWritePath); if (!testWriteFile.open(QFile::ReadWrite)) { QString msg = QString(tr("The directory at '%1' must be writable for the Amuse editor.")).arg(path); QMessageBox::critical(this, tr("Unable to write to directory"), msg); return false; } testWriteFile.remove(); closeEditor(); if (m_projectModel) m_projectModel->deleteLater(); m_projectModel = new ProjectModel(path, this); m_ui.projectOutline->setModel(m_projectModel); connect(m_ui.projectOutline->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(onOutlineSelectionChanged(const QItemSelection&, const QItemSelection&))); m_ui.actionSave_Project->setEnabled(true); m_ui.actionRevert_Project->setEnabled(true); m_ui.actionReload_Sample_Data->setEnabled(true); m_ui.actionExport_GameCube_Groups->setEnabled(true); setWindowFilePath(path); updateWindowTitle(); onFocusChanged(nullptr, focusWidget()); m_undoStack->clear(); QSettings settings; QStringList files = settings.value("recentFileList").toStringList(); files.removeAll(dir.path()); files.prepend(dir.path()); while (files.size() > MaxRecentFiles) files.removeLast(); settings.setValue("recentFileList", files); updateRecentFileActions(); return true; } void MainWindow::refreshAudioIO() { QList audioActions = m_ui.menuAudio->actions(); if (audioActions.size() > 3) for (auto it = audioActions.begin() + 3 ; it != audioActions.end() ; ++it) m_ui.menuAudio->removeAction(*it); bool addedDev = false; // TODO: Do if (!addedDev) m_ui.menuAudio->addAction(tr("No Audio Devices Found"))->setEnabled(false); } void MainWindow::refreshMIDIIO() { QList midiActions = m_ui.menuMIDI->actions(); if (midiActions.size() > 2) for (auto it = midiActions.begin() + 2 ; it != midiActions.end() ; ++it) m_ui.menuMIDI->removeAction(*it); bool addedDev = false; if (m_voxEngine) { for (const auto& dev : m_voxEngine->enumerateMIDIDevices()) { QAction* act = m_ui.menuMIDI->addAction(QString::fromStdString(dev.second)); act->setData(QString::fromStdString(dev.first)); connect(act, SIGNAL(triggered()), this, SLOT(setMIDIIO())); addedDev = true; } } if (!addedDev) m_ui.menuMIDI->addAction(tr("No MIDI Devices Found"))->setEnabled(false); } void MainWindow::timerEvent(QTimerEvent* ev) { if (m_voxEngine && m_engine) { m_voxEngine->pumpAndMixVoices(); m_ui.statusbar->setVoiceCount(int(m_engine->getActiveVoices().size())); if (m_engine->getActiveVoices().empty() && m_uiDisabled) { m_ui.projectOutline->setEnabled(true); m_ui.editorContents->setEnabled(true); m_ui.menubar->setEnabled(true); m_uiDisabled = false; } else if (!m_engine->getActiveVoices().empty() && !m_uiDisabled) { m_ui.projectOutline->setEnabled(false); m_ui.editorContents->setEnabled(false); m_ui.menubar->setEnabled(false); m_uiDisabled = true; } if (SampleEditor* sampleEditor = qobject_cast(m_ui.editorContents->currentWidget())) { const auto& activeVoxs = m_engine->getActiveVoices(); if (activeVoxs.size() && activeVoxs.back()->state() != amuse::VoiceState::Dead) sampleEditor->setSamplePos(activeVoxs.back()->getSamplePos()); else sampleEditor->setSamplePos(-1); } } } void MainWindow::setSustain(bool sustain) { if (sustain && m_ctrlVals[64] < 0x40) { m_ui.statusbar->setNormalMessage(tr("SUSTAIN")); for (auto& v : m_engine->getActiveVoices()) v->setPedal(true); m_ctrlVals[64] = 127; } else if (!sustain && m_ctrlVals[64] >= 0x40) { m_ui.statusbar->setNormalMessage({}); for (auto& v : m_engine->getActiveVoices()) v->setPedal(false); m_ctrlVals[64] = 0; } } void MainWindow::keyPressEvent(QKeyEvent* ev) { if (ev->key() == Qt::Key_Shift) setSustain(true); else if (ev->key() == Qt::Key_Escape) killSounds(); } void MainWindow::keyReleaseEvent(QKeyEvent* ev) { if (ev->key() == Qt::Key_Shift) setSustain(false); } void MainWindow::startBackgroundTask(const QString& windowTitle, const QString& label, std::function&& task) { assert(m_backgroundTask == nullptr && "existing background process"); setEnabled(false); m_backgroundTask = new BackgroundTask(std::move(task)); m_backgroundTask->moveToThread(&m_backgroundThread); m_backgroundDialog = new QProgressDialog(this); m_backgroundDialog->setWindowTitle(windowTitle); m_backgroundDialog->setLabelText(label); m_backgroundDialog->setMinimumWidth(300); m_backgroundDialog->setAutoClose(false); m_backgroundDialog->setAutoReset(false); m_backgroundDialog->setMinimumDuration(0); m_backgroundDialog->setMinimum(0); m_backgroundDialog->setMaximum(0); m_backgroundDialog->setValue(0); connect(m_backgroundTask, SIGNAL(setMinimum(int)), m_backgroundDialog, SLOT(setMinimum(int)), Qt::QueuedConnection); connect(m_backgroundTask, SIGNAL(setMaximum(int)), m_backgroundDialog, SLOT(setMaximum(int)), Qt::QueuedConnection); connect(m_backgroundTask, SIGNAL(setValue(int)), m_backgroundDialog, SLOT(setValue(int)), Qt::QueuedConnection); connect(m_backgroundTask, SIGNAL(setLabelText(const QString&)), m_backgroundDialog, SLOT(setLabelText(const QString&)), Qt::QueuedConnection); connect(m_backgroundTask, SIGNAL(finished()), this, SLOT(onBackgroundTaskFinished()), Qt::QueuedConnection); m_backgroundDialog->open(m_backgroundTask, SLOT(cancel())); connectMessenger(&m_backgroundTask->uiMessenger(), Qt::BlockingQueuedConnection); QMetaObject::invokeMethod(m_backgroundTask, "run", Qt::QueuedConnection); } bool MainWindow::_setEditor(EditorWidget* editor) { if (editor != m_ui.editorContents->currentWidget() && m_ui.editorContents->currentWidget() != m_faceSvg) static_cast(m_ui.editorContents->currentWidget())->unloadData(); if (!editor || !editor->valid()) { m_ui.editorContents->setCurrentWidget(m_faceSvg); updateWindowTitle(); return false; } m_ui.editorContents->setCurrentWidget(editor); m_ui.editorContents->update(); updateWindowTitle(); return true; } bool MainWindow::openEditor(ProjectModel::SongGroupNode* node) { return _setEditor(m_songGroupEditor->loadData(node) ? m_songGroupEditor : nullptr); } bool MainWindow::openEditor(ProjectModel::SoundGroupNode* node) { return _setEditor(m_soundGroupEditor->loadData(node) ? m_soundGroupEditor : nullptr); } bool MainWindow::openEditor(ProjectModel::SoundMacroNode* node) { return _setEditor(m_soundMacroEditor->loadData(node) ? m_soundMacroEditor : nullptr); } bool MainWindow::openEditor(ProjectModel::ADSRNode* node) { return _setEditor(m_adsrEditor->loadData(node) ? m_adsrEditor : nullptr); } bool MainWindow::openEditor(ProjectModel::CurveNode* node) { return _setEditor(m_curveEditor->loadData(node) ? m_curveEditor : nullptr); } bool MainWindow::openEditor(ProjectModel::KeymapNode* node) { return _setEditor(m_keymapEditor->loadData(node) ? m_keymapEditor : nullptr); } bool MainWindow::openEditor(ProjectModel::LayersNode* node) { return _setEditor(m_layersEditor->loadData(node) ? m_layersEditor : nullptr); } bool MainWindow::openEditor(ProjectModel::SampleNode* node) { return _setEditor(m_sampleEditor->loadData(node) ? m_sampleEditor : nullptr); } bool MainWindow::openEditor(ProjectModel::INode* node) { switch (node->type()) { case ProjectModel::INode::Type::SongGroup: return openEditor(static_cast(node)); case ProjectModel::INode::Type::SoundGroup: return openEditor(static_cast(node)); case ProjectModel::INode::Type::SoundMacro: return openEditor(static_cast(node)); case ProjectModel::INode::Type::ADSR: return openEditor(static_cast(node)); case ProjectModel::INode::Type::Curve: return openEditor(static_cast(node)); case ProjectModel::INode::Type::Keymap: return openEditor(static_cast(node)); case ProjectModel::INode::Type::Layer: return openEditor(static_cast(node)); case ProjectModel::INode::Type::Sample: return openEditor(static_cast(node)); default: return false; } } void MainWindow::closeEditor() { _setEditor(nullptr); } ProjectModel::INode* MainWindow::getEditorNode() const { if (m_ui.editorContents->currentWidget() != m_faceSvg) return static_cast(m_ui.editorContents->currentWidget())->currentNode(); return nullptr; } amuse::ObjToken MainWindow::startEditorVoice(uint8_t key, uint8_t velocity) { amuse::ObjToken vox; if (ProjectModel::INode* node = getEditorNode()) { amuse::AudioGroupDatabase* group = projectModel()->getGroupNode(node)->getAudioGroup(); if (node->type() == ProjectModel::INode::Type::SoundMacro) { ProjectModel::SoundMacroNode* cNode = static_cast(node); vox = m_engine->macroStart(group, cNode->id(), key, velocity, m_ctrlVals[1]); } else if (node->type() == ProjectModel::INode::Type::Sample) { SampleEditor* editor = static_cast(m_ui.editorContents->currentWidget()); vox = m_engine->macroStart(group, editor->soundMacro(), key, velocity, m_ctrlVals[1]); } if (vox) { vox->setPedal(m_ctrlVals[64] >= 0x40); vox->setPitchWheel(m_pitch); vox->installCtrlValues(m_ctrlVals); } } return vox; } void MainWindow::pushUndoCommand(QUndoCommand* cmd) { m_undoStack->push(cmd); } void MainWindow::aboutToDeleteNode(ProjectModel::INode* node) { if (getEditorNode() == node) closeEditor(); } void MainWindow::newAction() { QString path = QFileDialog::getSaveFileName(this, tr("New Project")); if (path.isEmpty()) return; if (!MkPath(path, m_mainMessenger)) return; if (!setProjectPath(path)) return; m_projectModel->clearProjectData(); m_projectModel->ensureModelData(); } bool MainWindow::openProject(const QString& path) { QDir dir(path); if (dir.path().isEmpty() || dir.path() == QStringLiteral(".") || dir.path() == QStringLiteral("..")) { QString msg = QString(tr("The directory at '%1' must not be empty.")).arg(path); QMessageBox::critical(this, tr("Directory empty"), msg); return false; } else if (!dir.exists()) { QString msg = QString(tr("The directory at '%1' does not exist.")).arg(path); QMessageBox::critical(this, tr("Bad Directory"), msg); return false; } if (QFileInfo(dir, QStringLiteral("!project.yaml")).exists() && QFileInfo(dir, QStringLiteral("!pool.yaml")).exists()) dir.cdUp(); if (m_projectModel && m_projectModel->path() == dir.path()) return true; if (!setProjectPath(dir.path())) return false; ProjectModel* model = m_projectModel; startBackgroundTask(tr("Opening"), tr("Scanning Project"), [dir, model](BackgroundTask& task) { QStringList childDirs = dir.entryList(QDir::Dirs); for (const auto& chDir : childDirs) { if (task.isCanceled()) return; QString chPath = dir.filePath(chDir); if (QFileInfo(chPath, QStringLiteral("!project.yaml")).exists() && QFileInfo(chPath, QStringLiteral("!pool.yaml")).exists()) { task.setLabelText(tr("Opening %1").arg(chDir)); if (!model->openGroupData(chDir, task.uiMessenger())) return; } } }); return true; } void MainWindow::openAction() { QString path = QFileDialog::getExistingDirectory(this, tr("Open Project")); if (path.isEmpty()) return; openProject(path); } void MainWindow::openRecentFileAction() { if (QAction *action = qobject_cast(sender())) if (!openProject(action->data().toString())) { QString path = action->data().toString(); QSettings settings; QStringList files = settings.value("recentFileList").toStringList(); files.removeAll(path); settings.setValue("recentFileList", files); updateRecentFileActions(); } } void MainWindow::clearRecentFilesAction() { QSettings settings; settings.setValue("recentFileList", QStringList()); updateRecentFileActions(); } void MainWindow::saveAction() { } void MainWindow::revertAction() { QString path = m_projectModel->path(); closeEditor(); m_undoStack->clear(); delete m_projectModel; m_projectModel = nullptr; openProject(path); } void MainWindow::reloadSampleDataAction() { closeEditor(); ProjectModel* model = m_projectModel; if (!m_projectModel) return; QDir dir(m_projectModel->path()); if (!dir.exists()) return; startBackgroundTask(tr("Reloading Samples"), tr("Scanning Project"), [dir, model](BackgroundTask& task) { QStringList childDirs = dir.entryList(QDir::Dirs); for (const auto& chDir : childDirs) { if (task.isCanceled()) return; QString chPath = dir.filePath(chDir); if (QFileInfo(chPath, QStringLiteral("!project.yaml")).exists() && QFileInfo(chPath, QStringLiteral("!pool.yaml")).exists()) { task.setLabelText(tr("Scanning %1").arg(chDir)); if (!model->reloadSampleData(chDir, task.uiMessenger())) return; } } }); } void MainWindow::importAction() { QString path = QFileDialog::getOpenFileName(this, tr("Import Project")); if (path.isEmpty()) return; /* Validate input file */ amuse::ContainerRegistry::Type tp = amuse::ContainerRegistry::DetectContainerType(QStringToSysString(path).c_str()); if (tp == amuse::ContainerRegistry::Type::Invalid) { QString msg = QString(tr("The file at '%1' could not be interpreted as a MusyX container.")).arg(path); QMessageBox::critical(this, tr("Unsupported MusyX Container"), msg); return; } /* Ask user about sample conversion */ int impMode = QMessageBox::question(this, tr("Sample Import Mode"), tr("Amuse can import samples as WAV files for ease of editing, " "import original compressed data for lossless repacking, or both. " "Exporting the project will prefer whichever version was modified " "most recently."), tr("Import Compressed"), tr("Import WAVs"), tr("Import Both")); switch (impMode) { case 0: case 1: case 2: break; default: return; } ProjectModel::ImportMode importMode = ProjectModel::ImportMode(impMode); /* Special handling for raw groups - gather sibling groups in filesystem */ if (tp == amuse::ContainerRegistry::Type::Raw4) { int scanMode = QMessageBox::question(this, tr("Raw Import Mode"), tr("Would you like to scan for all MusyX group files in this directory?"), QMessageBox::Yes, QMessageBox::No); if (scanMode == QMessageBox::Yes) { /* Auto-create project */ if (m_projectModel == nullptr) { QString newName; bool ok = true; while (ok && newName.isEmpty()) newName = QInputDialog::getText(this, tr("Project Name"), tr("What should this project be named?"), QLineEdit::Normal, QString(), &ok); if (!ok) return; QFileInfo fInfo(path); QString newPath = QFileInfo(fInfo.dir(), newName).filePath(); if (!MkPath(fInfo.dir(), newName, m_mainMessenger)) return; if (!setProjectPath(newPath)) return; } ProjectModel* model = m_projectModel; startBackgroundTask(tr("Importing"), tr("Scanning Project"), [model, path, importMode](BackgroundTask& task) { QDir dir = QFileInfo(path).dir(); QStringList filters; filters << "*.proj" << "*.pro"; QStringList files = dir.entryList(filters, QDir::Files); for (const QString& fPath : files) { auto data = amuse::ContainerRegistry::LoadContainer(QStringToSysString(dir.filePath(fPath)).c_str()); for (auto& p : data) { task.setLabelText(tr("Importing %1").arg(SysStringToQString(p.first))); if (task.isCanceled()) return; if (!model->importGroupData(SysStringToQString(p.first), p.second, importMode, task.uiMessenger())) return; } } }); return; } else if (scanMode == QMessageBox::No) {} else { return; } } /* Auto-create project */ if (m_projectModel == nullptr) { QFileInfo fInfo(path); QString newPath = QFileInfo(fInfo.dir(), fInfo.completeBaseName()).filePath(); if (!MkPath(fInfo.dir(), fInfo.completeBaseName(), m_mainMessenger)) return; if (!setProjectPath(newPath)) return; } ProjectModel* model = m_projectModel; startBackgroundTask(tr("Importing"), tr("Scanning Project"), [model, path, importMode](BackgroundTask& task) { /* Handle single container */ auto data = amuse::ContainerRegistry::LoadContainer(QStringToSysString(path).c_str()); task.setMaximum(int(data.size())); int curVal = 0; for (auto& p : data) { task.setLabelText(tr("Importing %1").arg(SysStringToQString(p.first))); if (task.isCanceled()) return; if (!model->importGroupData(SysStringToQString(p.first), p.second, importMode, task.uiMessenger())) return; task.setValue(++curVal); } }); } void MainWindow::exportAction() { } bool TreeDelegate::editorEvent(QEvent* event, QAbstractItemModel* _model, const QStyleOptionViewItem& option, const QModelIndex& index) { ProjectModel* model = static_cast(_model); ProjectModel::INode* node = model->node(index); if (!node) return false; if ((event->type() == QEvent::MouseButtonDblClick && static_cast(event)->button() == Qt::LeftButton)) { // Open in editor return m_window.openEditor(node); } return false; } void MainWindow::newSubprojectAction() { } void MainWindow::newSFXGroupAction() { } void MainWindow::newSongGroupAction() { } void MainWindow::newSoundMacroAction() { } void MainWindow::newADSRAction() { } void MainWindow::newCurveAction() { } void MainWindow::newKeymapAction() { } void MainWindow::newLayersAction() { } void MainWindow::aboutToShowAudioIOMenu() { refreshAudioIO(); } void MainWindow::aboutToShowMIDIIOMenu() { refreshMIDIIO(); } void MainWindow::setAudioIO() { // TODO: Do } void MainWindow::setMIDIIO() { // TODO: Do //qobject_cast(sender())->data().toString().toUtf8().constData(); } void MainWindow::notePressed(int key) { if (m_engine) m_lastSound = startEditorVoice(key, m_velocity); } void MainWindow::noteReleased() { if (m_lastSound) { m_lastSound->keyOff(); m_lastSound.reset(); } } void MainWindow::velocityChanged(int vel) { m_velocity = vel; } void MainWindow::modulationChanged(int mod) { m_ctrlVals[1] = int8_t(mod); for (auto& v : m_engine->getActiveVoices()) v->setCtrlValue(1, m_ctrlVals[1]); } void MainWindow::pitchChanged(int pitch) { m_pitch = pitch / 2048.f; for (auto& v : m_engine->getActiveVoices()) v->setPitchWheel(m_pitch); } void MainWindow::killSounds() { for (auto& v : m_engine->getActiveVoices()) v->kill(); } void MainWindow::outlineCutAction() { } void MainWindow::outlineCopyAction() { } void MainWindow::outlinePasteAction() { } void MainWindow::outlineDeleteAction() { if (!m_projectModel) return; QModelIndexList indexes = m_ui.projectOutline->selectionModel()->selectedIndexes(); if (indexes.empty()) return; m_projectModel->del(indexes.front()); } void MainWindow::onFocusChanged(QWidget* old, QWidget* now) { disconnect(m_cutConn); disconnect(m_copyConn); disconnect(m_pasteConn); disconnect(m_deleteConn); disconnect(m_canEditConn); if (QLineEdit* le = qobject_cast(now)) { m_cutConn = connect(m_ui.actionCut, SIGNAL(triggered()), le, SLOT(cut())); m_ui.actionCut->setEnabled(le->hasSelectedText()); m_copyConn = connect(m_ui.actionCopy, SIGNAL(triggered()), le, SLOT(copy())); m_ui.actionCopy->setEnabled(le->hasSelectedText()); m_pasteConn = connect(m_ui.actionPaste, SIGNAL(triggered()), le, SLOT(paste())); m_ui.actionPaste->setEnabled(true); m_deleteConn = connect(m_ui.actionDelete, SIGNAL(triggered()), this, SLOT(onTextDelete())); m_ui.actionDelete->setEnabled(true); m_canEditConn = connect(le, SIGNAL(selectionChanged()), this, SLOT(onTextSelect())); return; } if (now == m_ui.projectOutline || m_ui.projectOutline->isAncestorOf(now)) { setOutlineEditEnabled(canEditOutline()); if (m_projectModel) { m_cutConn = connect(m_ui.actionCut, SIGNAL(triggered()), this, SLOT(outlineCutAction())); m_copyConn = connect(m_ui.actionCopy, SIGNAL(triggered()), this, SLOT(outlineCopyAction())); m_pasteConn = connect(m_ui.actionPaste, SIGNAL(triggered()), this, SLOT(outlinePasteAction())); m_deleteConn = connect(m_ui.actionDelete, SIGNAL(triggered()), this, SLOT(outlineDeleteAction())); } } else if (now == m_ui.editorContents || m_ui.editorContents->isAncestorOf(now)) { setOutlineEditEnabled(false); } } void MainWindow::setOutlineEditEnabled(bool enabled) { m_ui.actionCut->setEnabled(enabled); m_ui.actionCopy->setEnabled(enabled); m_ui.actionPaste->setEnabled(enabled); m_ui.actionDelete->setEnabled(enabled); } bool MainWindow::canEditOutline() { if (!m_projectModel) return false; QModelIndexList indexes = m_ui.projectOutline->selectionModel()->selectedIndexes(); if (indexes.empty()) return false; return m_projectModel->canEdit(indexes.front()); } void MainWindow::onOutlineSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { if (!m_projectModel) return; if (selected.indexes().empty()) { setOutlineEditEnabled(false); return; } setOutlineEditEnabled(m_projectModel->canEdit(selected.indexes().front())); } void MainWindow::onTextSelect() { if (QLineEdit* le = qobject_cast(sender())) { m_ui.actionCut->setEnabled(le->hasSelectedText()); m_ui.actionCopy->setEnabled(le->hasSelectedText()); } } void MainWindow::onTextDelete() { if (QLineEdit* le = qobject_cast(focusWidget())) { le->del(); } } void MainWindow::onBackgroundTaskFinished() { m_backgroundDialog->reset(); m_backgroundDialog->deleteLater(); m_backgroundDialog = nullptr; m_backgroundTask->deleteLater(); m_backgroundTask = nullptr; m_projectModel->ensureModelData(); setEnabled(true); } QMessageBox::StandardButton MainWindow::msgInformation(const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { return QMessageBox::information(this, title, text, buttons, defaultButton); } QMessageBox::StandardButton MainWindow::msgQuestion(const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { return QMessageBox::question(this, title, text, buttons, defaultButton); } QMessageBox::StandardButton MainWindow::msgWarning(const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { return QMessageBox::warning(this, title, text, buttons, defaultButton); } QMessageBox::StandardButton MainWindow::msgCritical(const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { return QMessageBox::critical(this, title, text, buttons, defaultButton); }