Vastly improved node insertion/deletion

This commit is contained in:
Jack Andersen 2018-08-10 20:31:10 -10:00
parent 45cb6be3c5
commit 277e78c14b
6 changed files with 816 additions and 503 deletions

View File

@ -24,6 +24,7 @@ MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent),
m_treeDelegate(*this, this),
m_mainMessenger(this),
m_filterProjectModel(this),
m_undoStack(new QUndoStack(this)),
m_backgroundThread(this)
{
@ -31,6 +32,13 @@ MainWindow::MainWindow(QWidget* parent)
m_ui.setupUi(this);
m_ui.splitter->setCollapsible(1, false);
QPalette palette = m_ui.projectOutlineFilter->palette();
palette.setColor(QPalette::Base, palette.color(QPalette::Background));
m_ui.projectOutlineFilter->setPalette(palette);
connect(m_ui.projectOutlineFilter, SIGNAL(textChanged(const QString&)),
&m_filterProjectModel, SLOT(setFilterRegExp(const QString&)));
m_filterProjectModel.setRecursiveFilteringEnabled(true);
m_filterProjectModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
m_ui.projectOutline->setItemDelegate(&m_treeDelegate);
connect(m_ui.projectOutline, SIGNAL(activated(const QModelIndex&)),
this, SLOT(outlineItemActivated(const QModelIndex&)));
@ -253,7 +261,8 @@ bool MainWindow::setProjectPath(const QString& path)
if (m_projectModel)
m_projectModel->deleteLater();
m_projectModel = new ProjectModel(path, this);
m_ui.projectOutline->setModel(m_projectModel);
m_filterProjectModel.setSourceModel(m_projectModel);
m_ui.projectOutline->setModel(&m_filterProjectModel);
connect(m_ui.projectOutline->selectionModel(),
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
this, SLOT(onOutlineSelectionChanged(const QItemSelection&, const QItemSelection&)));
@ -957,22 +966,23 @@ QString MainWindow::getSelectedGroupName() const
return getGroupName(getSelectedGroupNode());
}
void MainWindow::recursiveExpandOutline(const QModelIndex& index) const
void MainWindow::_recursiveExpandOutline(const QModelIndex& filterIndex) const
{
if (index.isValid())
if (filterIndex.isValid())
{
recursiveExpandAndSelectOutline(index.parent());
m_ui.projectOutline->expand(index);
_recursiveExpandOutline(filterIndex.parent());
m_ui.projectOutline->expand(filterIndex);
}
}
void MainWindow::recursiveExpandAndSelectOutline(const QModelIndex& index) const
{
recursiveExpandOutline(index);
if (index.isValid())
QModelIndex filterIndex = m_filterProjectModel.mapFromSource(index);
_recursiveExpandOutline(filterIndex);
if (filterIndex.isValid())
{
m_ui.projectOutline->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
m_ui.projectOutline->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Current);
m_ui.projectOutline->selectionModel()->setCurrentIndex(filterIndex, QItemSelectionModel::ClearAndSelect);
m_ui.projectOutline->selectionModel()->setCurrentIndex(filterIndex, QItemSelectionModel::Current);
}
}
@ -986,9 +996,7 @@ void MainWindow::newSubprojectAction()
if (!ok)
return;
ProjectModel::GroupNode* node = m_projectModel->newSubproject(newName, m_mainMessenger);
if (node)
recursiveExpandAndSelectOutline(m_projectModel->index(node));
ProjectModel::GroupNode* node = m_projectModel->newSubproject(newName);
}
void MainWindow::newSFXGroupAction()
@ -1005,12 +1013,9 @@ void MainWindow::newSFXGroupAction()
if (!ok)
return;
ProjectModel::SoundGroupNode* node = m_projectModel->newSoundGroup(group, newName, m_mainMessenger);
ProjectModel::SoundGroupNode* node = m_projectModel->newSoundGroup(group, newName);
if (node)
{
recursiveExpandAndSelectOutline(m_projectModel->index(node));
openEditor(node);
}
}
void MainWindow::newSongGroupAction()
@ -1027,12 +1032,9 @@ void MainWindow::newSongGroupAction()
if (!ok)
return;
ProjectModel::SongGroupNode* node = m_projectModel->newSongGroup(group, newName, m_mainMessenger);
ProjectModel::SongGroupNode* node = m_projectModel->newSongGroup(group, newName);
if (node)
{
recursiveExpandAndSelectOutline(m_projectModel->index(node));
openEditor(node);
}
}
void MainWindow::newSoundMacroAction()
@ -1053,12 +1055,9 @@ void MainWindow::newSoundMacroAction()
if (result == QDialog::Rejected)
return;
ProjectModel::SoundMacroNode* node = m_projectModel->newSoundMacro(group, newName, m_mainMessenger, templ);
ProjectModel::SoundMacroNode* node = m_projectModel->newSoundMacro(group, newName, templ);
if (node)
{
recursiveExpandAndSelectOutline(m_projectModel->index(node));
openEditor(node);
}
}
void MainWindow::newADSRAction()
@ -1075,12 +1074,9 @@ void MainWindow::newADSRAction()
if (!ok)
return;
ProjectModel::ADSRNode* node = m_projectModel->newADSR(group, newName, m_mainMessenger);
ProjectModel::ADSRNode* node = m_projectModel->newADSR(group, newName);
if (node)
{
recursiveExpandAndSelectOutline(m_projectModel->index(node));
openEditor(node);
}
}
void MainWindow::newCurveAction()
@ -1097,12 +1093,9 @@ void MainWindow::newCurveAction()
if (!ok)
return;
ProjectModel::CurveNode* node = m_projectModel->newCurve(group, newName, m_mainMessenger);
ProjectModel::CurveNode* node = m_projectModel->newCurve(group, newName);
if (node)
{
recursiveExpandAndSelectOutline(m_projectModel->index(node));
openEditor(node);
}
}
void MainWindow::newKeymapAction()
@ -1119,12 +1112,9 @@ void MainWindow::newKeymapAction()
if (!ok)
return;
ProjectModel::KeymapNode* node = m_projectModel->newKeymap(group, newName, m_mainMessenger);
ProjectModel::KeymapNode* node = m_projectModel->newKeymap(group, newName);
if (node)
{
recursiveExpandAndSelectOutline(m_projectModel->index(node));
openEditor(node);
}
}
void MainWindow::newLayersAction()
@ -1141,12 +1131,9 @@ void MainWindow::newLayersAction()
if (!ok)
return;
ProjectModel::LayersNode* node = m_projectModel->newLayers(group, newName, m_mainMessenger);
ProjectModel::LayersNode* node = m_projectModel->newLayers(group, newName);
if (node)
{
recursiveExpandAndSelectOutline(m_projectModel->index(node));
openEditor(node);
}
}
void MainWindow::aboutToShowAudioIOMenu()
@ -1236,7 +1223,7 @@ void MainWindow::outlineDeleteAction()
QModelIndexList indexes = m_ui.projectOutline->selectionModel()->selectedIndexes();
if (indexes.empty())
return;
m_projectModel->del(indexes.front());
m_projectModel->del(m_filterProjectModel.mapToSource(indexes.front()));
}
void MainWindow::onFocusChanged(QWidget* old, QWidget* now)
@ -1292,7 +1279,7 @@ void MainWindow::onFocusChanged(QWidget* old, QWidget* now)
void MainWindow::outlineItemActivated(const QModelIndex& index)
{
ProjectModel::INode* node = m_projectModel->node(index);
ProjectModel::INode* node = m_projectModel->node(m_filterProjectModel.mapToSource(index));
if (!node)
return;
openEditor(node);
@ -1324,7 +1311,7 @@ bool MainWindow::canEditOutline()
QModelIndex curIndex = m_ui.projectOutline->selectionModel()->currentIndex();
if (!curIndex.isValid())
return false;
return m_projectModel->canEdit(curIndex);
return m_projectModel->canEdit(m_filterProjectModel.mapToSource(curIndex));
}
void MainWindow::onOutlineSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
@ -1335,7 +1322,7 @@ void MainWindow::onOutlineSelectionChanged(const QItemSelection& selected, const
if (selected.indexes().empty())
setItemEditEnabled(false);
else
setItemEditEnabled(m_projectModel->canEdit(selected.indexes().front()));
setItemEditEnabled(m_projectModel->canEdit(m_filterProjectModel.mapToSource(selected.indexes().front())));
}
void MainWindow::onTextSelect()

View File

@ -6,6 +6,7 @@
#include <QProgressDialog>
#include <QThread>
#include <QStyledItemDelegate>
#include <QSortFilterProxyModel>
#include "ui_MainWindow.h"
#include "amuse/Engine.hpp"
#include "amuse/BooBackend.hpp"
@ -78,6 +79,7 @@ class MainWindow : public QMainWindow
TreeDelegate m_treeDelegate;
UIMessenger m_mainMessenger;
ProjectModel* m_projectModel = nullptr;
QSortFilterProxyModel m_filterProjectModel;
QWidget* m_faceSvg;
SongGroupEditor* m_songGroupEditor = nullptr;
SoundGroupEditor* m_soundGroupEditor = nullptr;
@ -156,10 +158,11 @@ public:
QString getGroupName(ProjectModel::GroupNode* group) const;
ProjectModel::GroupNode* getSelectedGroupNode() const;
QString getSelectedGroupName() const;
void recursiveExpandOutline(const QModelIndex& index) const;
void _recursiveExpandOutline(const QModelIndex& filterIndex) const;
void recursiveExpandAndSelectOutline(const QModelIndex& index) const;
ProjectModel* projectModel() const { return m_projectModel; }
UIMessenger& uiMessenger() { return m_mainMessenger; }
public slots:
void newAction();

View File

@ -47,25 +47,53 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTreeView" name="projectOutline">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>3</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="animated">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<widget class="QWidget" name="projectOutlineContainer">
<layout class="QVBoxLayout" name="outlineLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="projectOutlineFilter">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="placeholderText">
<string>Filter</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="projectOutline">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="animated">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
<widget class="QSplitter" name="rightSplitter">
<property name="sizePolicy">

View File

@ -169,7 +169,11 @@ QModelIndex PageObjectProxyModel::parent(const QModelIndex& child) const
int PageObjectProxyModel::rowCount(const QModelIndex& parent) const
{
ProjectModel::INode* node = static_cast<ProjectModel*>(sourceModel())->node(parent);
ProjectModel::INode* node;
if (!parent.isValid())
node = static_cast<ProjectModel*>(sourceModel())->node(parent);
else
node = static_cast<ProjectModel::INode*>(parent.internalPointer());
auto tp = node->type();
if (tp != ProjectModel::INode::Type::Group)
return static_cast<ProjectModel*>(sourceModel())->rowCount(parent);
@ -265,12 +269,34 @@ Qt::ItemFlags PageObjectProxyModel::flags(const QModelIndex& proxyIndex) const
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
ProjectModel::INode::INode(INode* parent, int row) : m_parent(parent), m_row(row)
ProjectModel::INode::INode(const QString& name)
: m_name(name)
{
auto nullNode = amuse::MakeObj<NullNode>(this);
m_nullChild = nullNode.get();
}
int ProjectModel::INode::hypotheticalIndex(const QString& name) const
{
auto search = std::lower_bound(m_children.cbegin(), m_children.cend(), name,
[](const amuse::IObjToken<INode>& item, const QString& name)
{
return item->name() < name;
});
return int(search - m_children.cbegin());
}
int ProjectModel::GroupNode::hypotheticalIndex(const QString& name) const
{
/* Insert prior to pool object collections */
auto search = std::lower_bound(m_children.cbegin(), m_children.cend() - 6, name,
[](const amuse::IObjToken<INode>& item, const QString& name)
{
return item->name() < name;
});
return int(search - m_children.cbegin());
}
ProjectModel::CollectionNode* ProjectModel::GroupNode::getCollectionOfType(Type tp) const
{
for (auto it = m_children.rbegin(); it != m_children.rend(); ++it)
@ -336,38 +362,10 @@ ProjectModel::ProjectModel(const QString& path, QObject* parent)
SoundGroupNode::Icon = QIcon(":/icons/IconSoundGroup.svg");
}
void ProjectModel::_buildSortedList()
{
m_sorted.clear();
m_sorted.reserve(m_groups.size());
for (auto it = m_groups.begin() ; it != m_groups.end() ; ++it)
m_sorted.emplace_back(it);
std::sort(m_sorted.begin(), m_sorted.end());
}
QModelIndex ProjectModel::_indexOfGroup(const QString& groupName) const
{
auto search = std::lower_bound(m_sorted.cbegin(), m_sorted.cend(), groupName);
if (search == m_sorted.cend() || search->m_it->first != groupName)
return QModelIndex();
else
{
int idx = search - m_sorted.begin();
return createIndex(idx, 0, m_root->child(idx));
}
}
int ProjectModel::_hypotheticalIndexOfGroup(const QString& groupName) const
{
auto search = std::lower_bound(m_sorted.cbegin(), m_sorted.cend(), groupName);
return search - m_sorted.begin();
}
bool ProjectModel::clearProjectData()
{
m_projectDatabase = amuse::ProjectDatabase();
m_groups.clear();
m_sorted.clear();
m_midiFiles.clear();
m_needsReset = true;
@ -518,7 +516,7 @@ void ProjectModel::_buildGroupNode(GroupNode& gn)
gn.makeChild<SoundGroupNode>(grp.first, grp.second.get());
{
CollectionNode& col =
gn.makeChild<CollectionNode>(tr("Sound Macros"), QIcon(":/icons/IconSoundMacro.svg"), INode::Type::SoundMacro);
gn._appendChild<CollectionNode>(tr("Sound Macros"), QIcon(":/icons/IconSoundMacro.svg"), INode::Type::SoundMacro);
col.reserve(soundMacros.size());
for (const auto& macro : SortUnorderedMap(soundMacros))
col.makeChild<SoundMacroNode>(macro.first, macro.second.get());
@ -537,7 +535,7 @@ void ProjectModel::_buildGroupNode(GroupNode& gn)
}
{
CollectionNode& col =
gn.makeChild<CollectionNode>(tr("ADSRs"), QIcon(":/icons/IconADSR.svg"), INode::Type::ADSR);
gn._appendChild<CollectionNode>(tr("ADSRs"), QIcon(":/icons/IconADSR.svg"), INode::Type::ADSR);
col.reserve(ADSRCount);
for (auto& t : tablesSort)
{
@ -548,7 +546,7 @@ void ProjectModel::_buildGroupNode(GroupNode& gn)
}
{
CollectionNode& col =
gn.makeChild<CollectionNode>(tr("Curves"), QIcon(":/icons/IconCurve.svg"), INode::Type::Curve);
gn._appendChild<CollectionNode>(tr("Curves"), QIcon(":/icons/IconCurve.svg"), INode::Type::Curve);
col.reserve(curveCount);
for (auto& t : tablesSort)
{
@ -560,21 +558,21 @@ void ProjectModel::_buildGroupNode(GroupNode& gn)
}
{
CollectionNode& col =
gn.makeChild<CollectionNode>(tr("Keymaps"), QIcon(":/icons/IconKeymap.svg"), INode::Type::Keymap);
gn._appendChild<CollectionNode>(tr("Keymaps"), QIcon(":/icons/IconKeymap.svg"), INode::Type::Keymap);
col.reserve(keymaps.size());
for (auto& keymap : SortUnorderedMap(keymaps))
col.makeChild<KeymapNode>(keymap.first, keymap.second.get());
}
{
CollectionNode& col =
gn.makeChild<CollectionNode>(tr("Layers"), QIcon(":/icons/IconLayers.svg"), INode::Type::Layer);
gn._appendChild<CollectionNode>(tr("Layers"), QIcon(":/icons/IconLayers.svg"), INode::Type::Layer);
col.reserve(layers.size());
for (auto& keymap : SortUnorderedMap(layers))
col.makeChild<LayersNode>(keymap.first, keymap.second.get());
}
{
CollectionNode& col =
gn.makeChild<CollectionNode>(tr("Samples"), QIcon(":/icons/IconSample.svg"), INode::Type::Sample);
gn._appendChild<CollectionNode>(tr("Samples"), QIcon(":/icons/IconSample.svg"), INode::Type::Sample);
col.reserve(samples.size());
for (auto& sample : SortUnorderedMap(samples))
col.makeChild<SampleNode>(sample.first, sample.second.get());
@ -584,14 +582,13 @@ void ProjectModel::_buildGroupNode(GroupNode& gn)
void ProjectModel::_resetModelData()
{
beginResetModel();
_buildSortedList();
m_projectDatabase.setIdDatabases();
m_root = amuse::MakeObj<RootNode>();
m_root->reserve(m_sorted.size());
for (auto it = m_sorted.begin() ; it != m_sorted.end() ; ++it)
m_root->reserve(m_groups.size());
for (auto it = m_groups.begin(); it != m_groups.end(); ++it)
{
it->m_it->second.setIdDatabases();
GroupNode& gn = m_root->makeChild<GroupNode>(it->m_it);
it->second.setIdDatabases();
GroupNode& gn = m_root->makeChild<GroupNode>(it);
_buildGroupNode(gn);
}
endResetModel();
@ -623,9 +620,14 @@ QModelIndex ProjectModel::index(int row, int column, const QModelIndex& parent)
{
INode* parentItem;
if (!parent.isValid())
{
parentItem = m_root.get();
}
else
{
assert(parent.model() == this && "Not ProjectModel");
parentItem = static_cast<INode*>(parent.internalPointer());
}
INode* childItem = parentItem->nullChild();
return createIndex(childItem->row(), column, childItem);
@ -636,9 +638,14 @@ QModelIndex ProjectModel::index(int row, int column, const QModelIndex& parent)
INode* parentItem;
if (!parent.isValid())
{
parentItem = m_root.get();
}
else
{
assert(parent.model() == this && "Not ProjectModel");
parentItem = static_cast<INode*>(parent.internalPointer());
}
INode* childItem = parentItem->child(row);
if (childItem)
@ -658,6 +665,7 @@ QModelIndex ProjectModel::parent(const QModelIndex& index) const
{
if (!index.isValid())
return QModelIndex();
assert(index.model() == this && "Not ProjectModel");
INode* childItem = static_cast<INode*>(index.internalPointer());
INode* parentItem = childItem->parent();
@ -689,6 +697,7 @@ QVariant ProjectModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
return QVariant();
assert(index.model() == this && "Not ProjectModel");
INode* item = static_cast<INode*>(index.internalPointer());
@ -707,6 +716,7 @@ Qt::ItemFlags ProjectModel::flags(const QModelIndex& index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
assert(index.model() == this && "Not ProjectModel");
return static_cast<INode*>(index.internalPointer())->flags();
}
@ -715,6 +725,7 @@ ProjectModel::INode* ProjectModel::node(const QModelIndex& index) const
{
if (!index.isValid())
return m_root.get();
assert(index.model() == this && "Not ProjectModel");
return static_cast<INode*>(index.internalPointer());
}
@ -731,241 +742,424 @@ bool ProjectModel::canEdit(const QModelIndex& index) const
{
if (!index.isValid())
return false;
assert(index.model() == this && "Not ProjectModel");
return (static_cast<INode*>(index.internalPointer())->flags() & Qt::ItemIsSelectable) != Qt::NoItemFlags;
}
class DeleteNodeUndoCommand : public QUndoCommand
void ProjectModel::_postAddNode(INode* n, const NameUndoRegistry& registry)
{
QModelIndex m_deleteIdx;
amuse::ObjToken<ProjectModel::INode> m_node;
ProjectModel::NameUndoRegistry m_nameReg;
public:
DeleteNodeUndoCommand(const QModelIndex& index)
: QUndoCommand(ProjectModel::tr("Delete %1").arg(index.data().toString())), m_deleteIdx(index) {}
void undo()
setIdDatabases(n);
n->depthTraverse([&registry](INode* node)
{
g_MainWindow->projectModel()->_undoDel(m_deleteIdx, std::move(m_node), m_nameReg);
m_node.reset();
m_nameReg.clear();
}
void redo()
{
m_node = g_MainWindow->projectModel()->_redoDel(m_deleteIdx, m_nameReg);
}
};
void ProjectModel::_undoDel(const QModelIndex& index, amuse::ObjToken<ProjectModel::INode> n, const NameUndoRegistry& nameReg)
{
beginInsertRows(index.parent(), index.row(), index.row());
node(index.parent())->insertChild(index.row(), n);
setIdDatabases(n.get());
n->depthTraverse([&nameReg](INode* node)
{
node->registerNames(nameReg);
node->registerNames(registry);
return true;
});
}
void ProjectModel::_preDelNode(INode* n, NameUndoRegistry& registry)
{
n->depthTraverse([&registry](INode* node)
{
g_MainWindow->aboutToDeleteNode(node);
node->unregisterNames(registry);
return true;
});
}
template <class NT>
class NodeUndoCommand : public QUndoCommand
{
protected:
amuse::ObjToken<ProjectModel::GroupNode> m_parent;
amuse::ObjToken<NT> m_node;
ProjectModel::NameUndoRegistry m_nameReg;
void add()
{
g_MainWindow->projectModel()->_addNode(m_node.get(), m_parent.get(), m_nameReg);
g_MainWindow->recursiveExpandAndSelectOutline(g_MainWindow->projectModel()->index(m_node.get()));
}
void del()
{
g_MainWindow->projectModel()->_delNode(m_node.get(), m_parent.get(), m_nameReg);
}
public:
explicit NodeUndoCommand(const QString& text, NT* node, ProjectModel::GroupNode* parent)
: QUndoCommand(text.arg(node->text())), m_parent(parent), m_node(node){}
};
template <class NT>
class NodeAddUndoCommand : public NodeUndoCommand<NT>
{
using base = NodeUndoCommand<NT>;
public:
explicit NodeAddUndoCommand(const QString& text, NT* node, ProjectModel::GroupNode* parent)
: NodeUndoCommand<NT>(text, node, parent) {}
void undo() { base::del(); }
void redo() { base::add(); }
};
template <class NT>
class NodeDelUndoCommand : public NodeUndoCommand<NT>
{
using base = NodeUndoCommand<NT>;
public:
explicit NodeDelUndoCommand(const QString& text, NT* node)
: NodeUndoCommand<NT>(text, node, g_MainWindow->projectModel()->getGroupNode(node)) {}
void undo() { base::add(); }
void redo() { base::del(); }
};
void ProjectModel::_addNode(GroupNode* node, GroupNode*, const NameUndoRegistry& registry)
{
int idx = m_root->hypotheticalIndex(node->name());
beginInsertRows(QModelIndex(), idx, idx);
QDir dir(QFileInfo(m_dir, node->name()).filePath());
node->m_it = m_groups.emplace(std::make_pair(node->name(),
amuse::AudioGroupDatabase(QStringToSysString(dir.path())))).first;
m_root->insertChild(node);
_postAddNode(node, registry);
endInsertRows();
}
amuse::ObjToken<ProjectModel::INode> ProjectModel::_redoDel(const QModelIndex& index, NameUndoRegistry& nameReg)
void ProjectModel::_delNode(GroupNode* node, GroupNode*, NameUndoRegistry& registry)
{
node(index)->depthTraverse([&nameReg](INode* node)
{
g_MainWindow->aboutToDeleteNode(node);
node->unregisterNames(nameReg);
return true;
});
beginRemoveRows(index.parent(), index.row(), index.row());
amuse::ObjToken<ProjectModel::INode> ret = node(index.parent())->removeChild(index.row());
int idx = node->row();
beginRemoveRows(QModelIndex(), idx, idx);
_preDelNode(node, registry);
m_groups.erase(node->m_it);
node->m_it = {};
m_root->removeChild(node);
endRemoveRows();
return ret;
}
ProjectModel::GroupNode* ProjectModel::newSubproject(const QString& name)
{
if (m_groups.find(name) != m_groups.cend())
{
g_MainWindow->uiMessenger().
critical(tr("Subproject Conflict"), tr("The subproject %1 is already defined").arg(name));
return nullptr;
}
auto node = amuse::MakeObj<GroupNode>(name);
_buildGroupNode(*node);
g_MainWindow->pushUndoCommand(new NodeAddUndoCommand(tr("Add Subproject %1"), node.get(), nullptr));
return node.get();
}
template <class NT, class T>
void ProjectModel::_addGroupNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container)
{
int idx = parent->hypotheticalIndex(node->name());
beginInsertRows(index(parent), idx, idx);
setIdDatabases(parent);
amuse::GroupId newId = amuse::GroupId::CurNameDB->generateId(amuse::NameDB::Type::Group);
amuse::GroupId::CurNameDB->registerPair(node->name().toUtf8().data(), newId);
container[newId] = node->m_index;
node->m_id = newId;
parent->insertChild(node);
_postAddNode(node, registry);
endInsertRows();
}
template <class NT, class T>
void ProjectModel::_delGroupNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container)
{
int idx = node->row();
beginRemoveRows(index(parent), idx, idx);
_preDelNode(node, registry);
setIdDatabases(parent);
amuse::GroupId::CurNameDB->remove(node->m_id);
container.erase(node->m_id);
node->m_id = {};
parent->removeChild(node);
endRemoveRows();
}
void ProjectModel::_addNode(SoundGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry)
{
_addGroupNode(node, parent, registry, parent->getAudioGroup()->getProj().sfxGroups());
}
void ProjectModel::_delNode(SoundGroupNode* node, GroupNode* parent, NameUndoRegistry& registry)
{
_delGroupNode(node, parent, registry, parent->getAudioGroup()->getProj().sfxGroups());
}
ProjectModel::SoundGroupNode* ProjectModel::newSoundGroup(GroupNode* group, const QString& name)
{
setIdDatabases(group);
if (amuse::GroupId::CurNameDB->m_stringToId.find(name.toUtf8().data()) !=
amuse::GroupId::CurNameDB->m_stringToId.cend())
{
g_MainWindow->uiMessenger().
critical(tr("Sound Group Conflict"), tr("The group %1 is already defined").arg(name));
return nullptr;
}
auto node = amuse::MakeObj<SoundGroupNode>(name, amuse::MakeObj<amuse::SFXGroupIndex>());
g_MainWindow->pushUndoCommand(new NodeAddUndoCommand(tr("Add Sound Group %1"), node.get(), group));
return node.get();
}
void ProjectModel::_addNode(SongGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry)
{
_addGroupNode(node, parent, registry, parent->getAudioGroup()->getProj().songGroups());
}
void ProjectModel::_delNode(SongGroupNode* node, GroupNode* parent, NameUndoRegistry& registry)
{
_delGroupNode(node, parent, registry, parent->getAudioGroup()->getProj().songGroups());
}
ProjectModel::SongGroupNode* ProjectModel::newSongGroup(GroupNode* group, const QString& name)
{
setIdDatabases(group);
if (amuse::GroupId::CurNameDB->m_stringToId.find(name.toUtf8().data()) !=
amuse::GroupId::CurNameDB->m_stringToId.cend())
{
g_MainWindow->uiMessenger().
critical(tr("Song Group Conflict"), tr("The group %1 is already defined").arg(name));
return nullptr;
}
auto node = amuse::MakeObj<SongGroupNode>(name, amuse::MakeObj<amuse::SongGroupIndex>());
g_MainWindow->pushUndoCommand(new NodeAddUndoCommand(tr("Add Song Group %1"), node.get(), group));
return node.get();
}
static constexpr ProjectModel::INode::Type GetINodeType(ProjectModel::SoundMacroNode*)
{ return ProjectModel::INode::Type::SoundMacro; }
static constexpr ProjectModel::INode::Type GetINodeType(ProjectModel::ADSRNode*)
{ return ProjectModel::INode::Type::ADSR; }
static constexpr ProjectModel::INode::Type GetINodeType(ProjectModel::CurveNode*)
{ return ProjectModel::INode::Type::Curve; }
static constexpr ProjectModel::INode::Type GetINodeType(ProjectModel::KeymapNode*)
{ return ProjectModel::INode::Type::Keymap; }
static constexpr ProjectModel::INode::Type GetINodeType(ProjectModel::LayersNode*)
{ return ProjectModel::INode::Type::Layer; }
static constexpr amuse::NameDB::Type GetNameDBType(ProjectModel::SoundMacroNode*)
{ return amuse::NameDB::Type::SoundMacro; }
static constexpr amuse::NameDB::Type GetNameDBType(ProjectModel::ADSRNode*)
{ return amuse::NameDB::Type::Table; }
static constexpr amuse::NameDB::Type GetNameDBType(ProjectModel::CurveNode*)
{ return amuse::NameDB::Type::Table; }
static constexpr amuse::NameDB::Type GetNameDBType(ProjectModel::KeymapNode*)
{ return amuse::NameDB::Type::Keymap; }
static constexpr amuse::NameDB::Type GetNameDBType(ProjectModel::LayersNode*)
{ return amuse::NameDB::Type::Layer; }
static amuse::NameDB* GetNameDB(ProjectModel::SoundMacroNode*) { return amuse::SoundMacroId::CurNameDB; }
static amuse::NameDB* GetNameDB(ProjectModel::ADSRNode*) { return amuse::TableId::CurNameDB; }
static amuse::NameDB* GetNameDB(ProjectModel::CurveNode*) { return amuse::TableId::CurNameDB; }
static amuse::NameDB* GetNameDB(ProjectModel::KeymapNode*) { return amuse::KeymapId::CurNameDB; }
static amuse::NameDB* GetNameDB(ProjectModel::LayersNode*) { return amuse::LayersId::CurNameDB; }
template <class NT, class T>
void ProjectModel::_addPoolNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container)
{
setIdDatabases(parent);
CollectionNode* coll = parent->getCollectionOfType(GetINodeType(node));
int insertIdx = coll->hypotheticalIndex(node->name());
beginInsertRows(index(coll), insertIdx, insertIdx);
auto newId = GetNameDB(node)->generateId(GetNameDBType(node));
GetNameDB(node)->registerPair(node->name().toUtf8().data(), newId);
container[newId] = node->m_obj;
node->m_id = newId;
coll->insertChild(node);
_postAddNode(node, registry);
endInsertRows();
}
template <class NT, class T>
void ProjectModel::_delPoolNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container)
{
int idx = node->row();
CollectionNode* coll = parent->getCollectionOfType(GetINodeType(node));
beginRemoveRows(index(coll), idx, idx);
_preDelNode(node, registry);
setIdDatabases(parent);
GetNameDB(node)->remove(node->m_id);
container.erase(node->m_id);
node->m_id = {};
coll->removeChild(node);
endRemoveRows();
}
void ProjectModel::_addNode(SoundMacroNode* node, GroupNode* parent, const NameUndoRegistry& registry)
{
_addPoolNode(node, parent, registry, parent->getAudioGroup()->getPool().soundMacros());
}
void ProjectModel::_delNode(SoundMacroNode* node, GroupNode* parent, NameUndoRegistry& registry)
{
_delPoolNode(node, parent, registry, parent->getAudioGroup()->getPool().soundMacros());
}
ProjectModel::SoundMacroNode* ProjectModel::newSoundMacro(GroupNode* group, const QString& name,
const SoundMacroTemplateEntry* templ)
{
setIdDatabases(group);
if (amuse::SoundMacroId::CurNameDB->m_stringToId.find(name.toUtf8().data()) !=
amuse::SoundMacroId::CurNameDB->m_stringToId.cend())
{
g_MainWindow->uiMessenger().
critical(tr("Sound Macro Conflict"), tr("The macro %1 is already defined").arg(name));
return nullptr;
}
auto dataNode = amuse::MakeObj<amuse::SoundMacro>();
if (templ)
{
athena::io::MemoryReader r(templ->m_data, templ->m_length);
dataNode->readCmds<athena::utility::NotSystemEndian>(r, templ->m_length);
}
auto node = amuse::MakeObj<SoundMacroNode>(name, dataNode);
g_MainWindow->pushUndoCommand(new NodeAddUndoCommand(tr("Add Sound Macro %1"), node.get(), group));
return node.get();
}
void ProjectModel::_addNode(ADSRNode* node, GroupNode* parent, const NameUndoRegistry& registry)
{
_addPoolNode(node, parent, registry, parent->getAudioGroup()->getPool().tables());
}
void ProjectModel::_delNode(ADSRNode* node, GroupNode* parent, NameUndoRegistry& registry)
{
_delPoolNode(node, parent, registry, parent->getAudioGroup()->getPool().tables());
}
ProjectModel::ADSRNode* ProjectModel::newADSR(GroupNode* group, const QString& name)
{
setIdDatabases(group);
if (amuse::TableId::CurNameDB->m_stringToId.find(name.toUtf8().data()) !=
amuse::TableId::CurNameDB->m_stringToId.cend())
{
g_MainWindow->uiMessenger().
critical(tr("ADSR Conflict"), tr("The ADSR %1 is already defined").arg(name));
return nullptr;
}
auto dataNode = amuse::MakeObj<std::unique_ptr<amuse::ITable>>();
*dataNode = std::make_unique<amuse::ADSR>();
auto node = amuse::MakeObj<ADSRNode>(name, dataNode);
g_MainWindow->pushUndoCommand(new NodeAddUndoCommand(tr("Add ADSR %1"), node.get(), group));
return node.get();
}
void ProjectModel::_addNode(CurveNode* node, GroupNode* parent, const NameUndoRegistry& registry)
{
_addPoolNode(node, parent, registry, parent->getAudioGroup()->getPool().tables());
}
void ProjectModel::_delNode(CurveNode* node, GroupNode* parent, NameUndoRegistry& registry)
{
_delPoolNode(node, parent, registry, parent->getAudioGroup()->getPool().tables());
}
ProjectModel::CurveNode* ProjectModel::newCurve(GroupNode* group, const QString& name)
{
setIdDatabases(group);
if (amuse::TableId::CurNameDB->m_stringToId.find(name.toUtf8().data()) !=
amuse::TableId::CurNameDB->m_stringToId.cend())
{
g_MainWindow->uiMessenger().
critical(tr("Curve Conflict"), tr("The Curve %1 is already defined").arg(name));
return nullptr;
}
auto dataNode = amuse::MakeObj<std::unique_ptr<amuse::ITable>>();
*dataNode = std::make_unique<amuse::Curve>();
auto node = amuse::MakeObj<CurveNode>(name, dataNode);
g_MainWindow->pushUndoCommand(new NodeAddUndoCommand(tr("Add Curve %1"), node.get(), group));
return node.get();
}
void ProjectModel::_addNode(KeymapNode* node, GroupNode* parent, const NameUndoRegistry& registry)
{
_addPoolNode(node, parent, registry, parent->getAudioGroup()->getPool().keymaps());
}
void ProjectModel::_delNode(KeymapNode* node, GroupNode* parent, NameUndoRegistry& registry)
{
_delPoolNode(node, parent, registry, parent->getAudioGroup()->getPool().keymaps());
}
ProjectModel::KeymapNode* ProjectModel::newKeymap(GroupNode* group, const QString& name)
{
setIdDatabases(group);
if (amuse::KeymapId::CurNameDB->m_stringToId.find(name.toUtf8().data()) !=
amuse::KeymapId::CurNameDB->m_stringToId.cend())
{
g_MainWindow->uiMessenger().
critical(tr("Keymap Conflict"), tr("The Keymap %1 is already defined").arg(name));
return nullptr;
}
auto dataNode = amuse::MakeObj<std::array<amuse::Keymap, 128>>();
auto node = amuse::MakeObj<KeymapNode>(name, dataNode);
g_MainWindow->pushUndoCommand(new NodeAddUndoCommand(tr("Add Keymap %1"), node.get(), group));
return node.get();
}
void ProjectModel::_addNode(LayersNode* node, GroupNode* parent, const NameUndoRegistry& registry)
{
_addPoolNode(node, parent, registry, parent->getAudioGroup()->getPool().layers());
}
void ProjectModel::_delNode(LayersNode* node, GroupNode* parent, NameUndoRegistry& registry)
{
_delPoolNode(node, parent, registry, parent->getAudioGroup()->getPool().layers());
}
ProjectModel::LayersNode* ProjectModel::newLayers(GroupNode* group, const QString& name)
{
setIdDatabases(group);
if (amuse::LayersId::CurNameDB->m_stringToId.find(name.toUtf8().data()) !=
amuse::LayersId::CurNameDB->m_stringToId.cend())
{
g_MainWindow->uiMessenger().
critical(tr("Layers Conflict"), tr("Layers %1 is already defined").arg(name));
return nullptr;
}
auto dataNode = amuse::MakeObj<std::vector<amuse::LayerMapping>>();
auto node = amuse::MakeObj<LayersNode>(name, dataNode);
g_MainWindow->pushUndoCommand(new NodeAddUndoCommand(tr("Add Layers %1"), node.get(), group));
return node.get();
}
void ProjectModel::del(const QModelIndex& index)
{
if (!index.isValid())
return;
g_MainWindow->pushUndoCommand(new DeleteNodeUndoCommand(index));
}
ProjectModel::GroupNode* ProjectModel::newSubproject(const QString& name, UIMessenger& messenger)
{
if (m_groups.find(name) != m_groups.cend())
assert(index.model() == this && "Not ProjectModel");
INode* n = node(index);
QUndoCommand* cmd = nullptr;
switch (n->type())
{
messenger.critical(tr("Subproject Conflict"), tr("The subproject %1 is already defined").arg(name));
return nullptr;
case INode::Type::Group:
cmd = new NodeDelUndoCommand(tr("Delete Subproject %1"), static_cast<GroupNode*>(n));
break;
case INode::Type::SongGroup:
cmd = new NodeDelUndoCommand(tr("Delete SongGroup %1"), static_cast<SongGroupNode*>(n));
break;
case INode::Type::SoundGroup:
cmd = new NodeDelUndoCommand(tr("Delete SFXGroup %1"), static_cast<SoundGroupNode*>(n));
break;
case INode::Type::SoundMacro:
cmd = new NodeDelUndoCommand(tr("Delete SoundMacro %1"), static_cast<SoundMacroNode*>(n));
break;
case INode::Type::ADSR:
cmd = new NodeDelUndoCommand(tr("Delete ADSR %1"), static_cast<ADSRNode*>(n));
break;
case INode::Type::Curve:
cmd = new NodeDelUndoCommand(tr("Delete Curve %1"), static_cast<CurveNode*>(n));
break;
case INode::Type::Keymap:
cmd = new NodeDelUndoCommand(tr("Delete Keymap %1"), static_cast<KeymapNode*>(n));
break;
case INode::Type::Layer:
cmd = new NodeDelUndoCommand(tr("Delete Layers %1"), static_cast<LayersNode*>(n));
break;
default:
break;
}
QDir dir(QFileInfo(m_dir, name).filePath());
if (!MkPath(dir.path(), messenger))
return nullptr;
QFile(QFileInfo(dir, QStringLiteral("!project.yaml")).filePath()).open(QFile::WriteOnly);
QFile(QFileInfo(dir, QStringLiteral("!pool.yaml")).filePath()).open(QFile::WriteOnly);
int idx = _hypotheticalIndexOfGroup(name);
beginInsertRows(QModelIndex(), idx, idx);
auto it = m_groups.emplace(std::make_pair(name, amuse::AudioGroupDatabase(QStringToSysString(dir.path())))).first;
_buildSortedList();
m_projectDatabase.setIdDatabases();
it->second.setIdDatabases();
GroupNode& gn = m_root->makeChildAtIdx<GroupNode>(idx, it);
_buildGroupNode(gn);
endInsertRows();
return &gn;
}
ProjectModel::SoundGroupNode* ProjectModel::newSoundGroup(GroupNode* group, const QString& name, UIMessenger& messenger)
{
setIdDatabases(group);
auto nameKey = name.toUtf8();
if (amuse::GroupId::CurNameDB->m_stringToId.find(nameKey.data()) != amuse::GroupId::CurNameDB->m_stringToId.cend())
{
messenger.critical(tr("Sound Group Conflict"), tr("The group %1 is already defined").arg(name));
return nullptr;
}
beginInsertRows(index(group), 0, 0);
amuse::GroupId newId = amuse::GroupId::CurNameDB->generateId(amuse::NameDB::Type::Group);
amuse::GroupId::CurNameDB->registerPair(nameKey.data(), newId);
auto node = amuse::MakeObj<amuse::SFXGroupIndex>();
group->getAudioGroup()->getProj().sfxGroups()[newId] = node;
SoundGroupNode& ret = group->makeChildAtIdx<SoundGroupNode>(0, newId, node);
endInsertRows();
return &ret;
}
ProjectModel::SongGroupNode* ProjectModel::newSongGroup(GroupNode* group, const QString& name, UIMessenger& messenger)
{
setIdDatabases(group);
auto nameKey = name.toUtf8();
if (amuse::GroupId::CurNameDB->m_stringToId.find(nameKey.data()) != amuse::GroupId::CurNameDB->m_stringToId.cend())
{
messenger.critical(tr("Song Group Conflict"), tr("The group %1 is already defined").arg(name));
return nullptr;
}
beginInsertRows(index(group), 0, 0);
amuse::GroupId newId = amuse::GroupId::CurNameDB->generateId(amuse::NameDB::Type::Group);
amuse::GroupId::CurNameDB->registerPair(nameKey.data(), newId);
auto node = amuse::MakeObj<amuse::SongGroupIndex>();
group->getAudioGroup()->getProj().songGroups()[newId] = node;
SongGroupNode& ret = group->makeChildAtIdx<SongGroupNode>(0, newId, node);
endInsertRows();
return &ret;
}
ProjectModel::SoundMacroNode* ProjectModel::newSoundMacro(GroupNode* group, const QString& name, UIMessenger& messenger,
const SoundMacroTemplateEntry* templ)
{
setIdDatabases(group);
auto nameKey = name.toUtf8();
if (amuse::SoundMacroId::CurNameDB->m_stringToId.find(nameKey.data()) != amuse::SoundMacroId::CurNameDB->m_stringToId.cend())
{
messenger.critical(tr("Sound Macro Conflict"), tr("The macro %1 is already defined").arg(name));
return nullptr;
}
ProjectModel::CollectionNode* coll = group->getCollectionOfType(INode::Type::SoundMacro);
QModelIndex parentIdx = index(coll);
int insertIdx = rowCount(parentIdx);
beginInsertRows(parentIdx, insertIdx, insertIdx);
amuse::SoundMacroId newId = amuse::SoundMacroId::CurNameDB->generateId(amuse::NameDB::Type::SoundMacro);
amuse::SoundMacroId::CurNameDB->registerPair(nameKey.data(), newId);
auto node = amuse::MakeObj<amuse::SoundMacro>();
if (templ)
{
athena::io::MemoryReader r(templ->m_data, templ->m_length);
node->readCmds<athena::utility::NotSystemEndian>(r, templ->m_length);
}
group->getAudioGroup()->getPool().soundMacros()[newId] = node;
SoundMacroNode& ret = coll->makeChildAtIdx<SoundMacroNode>(insertIdx, newId, node);
endInsertRows();
return &ret;
}
ProjectModel::ADSRNode* ProjectModel::newADSR(GroupNode* group, const QString& name, UIMessenger& messenger)
{
setIdDatabases(group);
auto nameKey = name.toUtf8();
if (amuse::TableId::CurNameDB->m_stringToId.find(nameKey.data()) != amuse::TableId::CurNameDB->m_stringToId.cend())
{
messenger.critical(tr("ADSR Conflict"), tr("The ADSR %1 is already defined").arg(name));
return nullptr;
}
ProjectModel::CollectionNode* coll = group->getCollectionOfType(INode::Type::ADSR);
QModelIndex parentIdx = index(coll);
int insertIdx = rowCount(parentIdx);
beginInsertRows(parentIdx, insertIdx, insertIdx);
amuse::TableId newId = amuse::TableId::CurNameDB->generateId(amuse::NameDB::Type::Table);
amuse::TableId::CurNameDB->registerPair(nameKey.data(), newId);
auto node = amuse::MakeObj<std::unique_ptr<amuse::ITable>>();
*node = std::make_unique<amuse::ADSR>();
group->getAudioGroup()->getPool().tables()[newId] = node;
ADSRNode& ret = coll->makeChildAtIdx<ADSRNode>(insertIdx, newId, node);
endInsertRows();
return &ret;
}
ProjectModel::CurveNode* ProjectModel::newCurve(GroupNode* group, const QString& name, UIMessenger& messenger)
{
setIdDatabases(group);
auto nameKey = name.toUtf8();
if (amuse::TableId::CurNameDB->m_stringToId.find(nameKey.data()) != amuse::TableId::CurNameDB->m_stringToId.cend())
{
messenger.critical(tr("Curve Conflict"), tr("The Curve %1 is already defined").arg(name));
return nullptr;
}
ProjectModel::CollectionNode* coll = group->getCollectionOfType(INode::Type::Curve);
QModelIndex parentIdx = index(coll);
int insertIdx = rowCount(parentIdx);
beginInsertRows(parentIdx, insertIdx, insertIdx);
amuse::TableId newId = amuse::TableId::CurNameDB->generateId(amuse::NameDB::Type::Table);
amuse::TableId::CurNameDB->registerPair(nameKey.data(), newId);
auto node = amuse::MakeObj<std::unique_ptr<amuse::ITable>>();
*node = std::make_unique<amuse::Curve>();
group->getAudioGroup()->getPool().tables()[newId] = node;
CurveNode& ret = coll->makeChildAtIdx<CurveNode>(insertIdx, newId, node);
endInsertRows();
return &ret;
}
ProjectModel::KeymapNode* ProjectModel::newKeymap(GroupNode* group, const QString& name, UIMessenger& messenger)
{
setIdDatabases(group);
auto nameKey = name.toUtf8();
if (amuse::KeymapId::CurNameDB->m_stringToId.find(nameKey.data()) != amuse::KeymapId::CurNameDB->m_stringToId.cend())
{
messenger.critical(tr("Keymap Conflict"), tr("The Keymap %1 is already defined").arg(name));
return nullptr;
}
ProjectModel::CollectionNode* coll = group->getCollectionOfType(INode::Type::Keymap);
QModelIndex parentIdx = index(coll);
int insertIdx = rowCount(parentIdx);
beginInsertRows(parentIdx, insertIdx, insertIdx);
amuse::KeymapId newId = amuse::KeymapId::CurNameDB->generateId(amuse::NameDB::Type::Keymap);
amuse::KeymapId::CurNameDB->registerPair(nameKey.data(), newId);
auto node = amuse::MakeObj<std::array<amuse::Keymap, 128>>();
group->getAudioGroup()->getPool().keymaps()[newId] = node;
KeymapNode& ret = coll->makeChildAtIdx<KeymapNode>(insertIdx, newId, node);
endInsertRows();
return &ret;
}
ProjectModel::LayersNode* ProjectModel::newLayers(GroupNode* group, const QString& name, UIMessenger& messenger)
{
setIdDatabases(group);
auto nameKey = name.toUtf8();
if (amuse::LayersId::CurNameDB->m_stringToId.find(nameKey.data()) != amuse::LayersId::CurNameDB->m_stringToId.cend())
{
messenger.critical(tr("Layers Conflict"), tr("Layers %1 is already defined").arg(name));
return nullptr;
}
ProjectModel::CollectionNode* coll = group->getCollectionOfType(INode::Type::Layer);
QModelIndex parentIdx = index(coll);
int insertIdx = rowCount(parentIdx);
beginInsertRows(parentIdx, insertIdx, insertIdx);
amuse::LayersId newId = amuse::LayersId::CurNameDB->generateId(amuse::NameDB::Type::Layer);
amuse::LayersId::CurNameDB->registerPair(nameKey.data(), newId);
auto node = amuse::MakeObj<std::vector<amuse::LayerMapping>>();
group->getAudioGroup()->getPool().layers()[newId] = node;
LayersNode& ret = coll->makeChildAtIdx<LayersNode>(insertIdx, newId, node);
endInsertRows();
return &ret;
if (cmd)
g_MainWindow->pushUndoCommand(cmd);
}
ProjectModel::GroupNode* ProjectModel::getGroupOfSfx(amuse::SFXId id) const

View File

@ -96,27 +96,7 @@ private:
PageObjectProxyModel m_pageObjectProxy;
amuse::ProjectDatabase m_projectDatabase;
std::unordered_map<QString, amuse::AudioGroupDatabase> m_groups;
struct Iterator
{
using ItTp = std::unordered_map<QString, amuse::AudioGroupDatabase>::iterator;
ItTp m_it;
Iterator(ItTp it) : m_it(it) {}
ItTp::pointer operator->() { return m_it.operator->(); }
bool operator<(const Iterator& other) const
{
return m_it->first < other.m_it->first;
}
bool operator<(const QString& name) const
{
return m_it->first < name;
}
};
std::vector<Iterator> m_sorted;
void _buildSortedList();
QModelIndex _indexOfGroup(const QString& groupName) const;
int _hypotheticalIndexOfGroup(const QString& groupName) const;
std::unordered_map<amuse::SongId, QString> m_midiFiles;
@ -140,17 +120,18 @@ public:
Sample
};
protected:
INode* m_parent;
QString m_name;
INode* m_parent = nullptr;
int m_row = -1;
std::vector<amuse::IObjToken<INode>> m_children;
amuse::IObjToken<INode> m_nullChild;
int m_row;
public:
virtual ~INode() = default;
INode(const QString& name);
INode(INode* parent) : m_parent(parent), m_row(0)
{
/* ONLY USED BY NULL NODE! */
}
INode(INode* parent, int row);
int childCount() const { return int(m_children.size()); }
INode* child(int row) const
@ -170,33 +151,41 @@ public:
m_nullChild->m_row = row;
}
void insertChild(int row, amuse::ObjToken<INode> n)
void insertChild(amuse::ObjToken<INode> n)
{
assert(n->m_parent == nullptr && "Inserting already-parented node");
n->m_parent = this;
int row = hypotheticalIndex(n->name());
m_children.insert(m_children.begin() + row, n.get());
reindexRows(row);
}
amuse::ObjToken<INode> removeChild(int row)
amuse::ObjToken<INode> removeChild(INode* n)
{
amuse::ObjToken<INode> ret = m_children[row].get();
int row = n->row();
assert(n == m_children.at(row).get() && "Removing non-child from node");
m_children.erase(m_children.begin() + row);
reindexRows(row);
return ret;
n->m_parent = nullptr;
n->m_row = -1;
return n;
}
void reserve(size_t sz) { m_children.reserve(sz); }
template<class T, class... _Args>
T& makeChild(_Args&&... args)
{
auto tok = amuse::MakeObj<T>(this, m_children.size(), std::forward<_Args>(args)...);
m_children.push_back(tok.get());
m_nullChild->m_row = int(m_children.size());
return static_cast<T&>(*m_children.back());
auto tok = amuse::MakeObj<T>(std::forward<_Args>(args)...);
insertChild(tok.get());
return static_cast<T&>(*tok);
}
template<class T, class... _Args>
T& makeChildAtIdx(int idx, _Args&&... args)
T& _appendChild(_Args&&... args)
{
auto tok = amuse::MakeObj<T>(this, idx, std::forward<_Args>(args)...);
insertChild(idx, tok.get());
auto tok = amuse::MakeObj<T>(std::forward<_Args>(args)...);
tok->m_parent = this;
tok->m_row = m_children.size();
m_children.push_back(tok.get());
m_nullChild->m_row = m_children.size();
return static_cast<T&>(*tok);
}
@ -216,6 +205,9 @@ public:
return true;
}
const QString& name() const { return m_name; }
virtual int hypotheticalIndex(const QString& name) const;
virtual Type type() const = 0;
virtual QString text() const = 0;
virtual QIcon icon() const = 0;
@ -234,7 +226,7 @@ public:
};
struct RootNode : INode
{
RootNode() : INode(nullptr, 0) {}
RootNode() : INode(QStringLiteral("<root>")) {}
Type type() const { return Type::Root; }
QString text() const { return {}; }
@ -246,12 +238,15 @@ public:
struct GroupNode : INode
{
std::unordered_map<QString, amuse::AudioGroupDatabase>::iterator m_it;
GroupNode(INode* parent, int row, std::unordered_map<QString, amuse::AudioGroupDatabase>::iterator it)
: INode(parent, row), m_it(it) {}
GroupNode(const QString& name) : INode(name) {}
GroupNode(std::unordered_map<QString, amuse::AudioGroupDatabase>::iterator it)
: INode(it->first), m_it(it) {}
int hypotheticalIndex(const QString& name) const;
static QIcon Icon;
Type type() const { return Type::Group; }
QString text() const { return m_it->first; }
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
CollectionNode* getCollectionOfType(Type tp) const;
@ -261,10 +256,11 @@ public:
struct SongGroupNode : INode
{
amuse::GroupId m_id;
QString m_name;
amuse::ObjToken<amuse::SongGroupIndex> m_index;
SongGroupNode(INode* parent, int row, amuse::GroupId id, amuse::ObjToken<amuse::SongGroupIndex> index)
: INode(parent, row), m_id(id), m_name(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_index(index) {}
SongGroupNode(const QString& name, amuse::ObjToken<amuse::SongGroupIndex> index)
: INode(name), m_index(index) {}
SongGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SongGroupIndex> index)
: INode(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_id(id), m_index(index) {}
static QIcon Icon;
Type type() const { return Type::SongGroup; }
@ -287,10 +283,11 @@ public:
struct SoundGroupNode : INode
{
amuse::GroupId m_id;
QString m_name;
amuse::ObjToken<amuse::SFXGroupIndex> m_index;
SoundGroupNode(INode* parent, int row, amuse::GroupId id, amuse::ObjToken<amuse::SFXGroupIndex> index)
: INode(parent, row), m_id(id), m_name(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_index(index) {}
SoundGroupNode(const QString& name, amuse::ObjToken<amuse::SFXGroupIndex> index)
: INode(name), m_index(index) {}
SoundGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SFXGroupIndex> index)
: INode(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_id(id), m_index(index) {}
static QIcon Icon;
Type type() const { return Type::SoundGroup; }
@ -312,11 +309,10 @@ public:
};
struct CollectionNode : INode
{
QString m_name;
QIcon m_icon;
Type m_collectionType;
CollectionNode(INode* parent, int row, const QString& name, const QIcon& icon, Type collectionType)
: INode(parent, row), m_name(name), m_icon(icon), m_collectionType(collectionType) {}
CollectionNode(const QString& name, const QIcon& icon, Type collectionType)
: INode(name), m_icon(icon), m_collectionType(collectionType) {}
Type type() const { return Type::Collection; }
QString text() const { return m_name; }
@ -332,9 +328,9 @@ public:
struct BasePoolObjectNode : INode
{
amuse::ObjectId m_id;
QString m_name;
BasePoolObjectNode(INode* parent, int row, amuse::ObjectId id, const QString& name)
: INode(parent, row), m_id(id), m_name(name) {}
BasePoolObjectNode(const QString& name) : INode(name) {}
BasePoolObjectNode(amuse::ObjectId id, const QString& name)
: INode(name), m_id(id) {}
amuse::ObjectId id() const { return m_id; }
QString text() const { return m_name; }
QIcon icon() const { return {}; }
@ -343,8 +339,9 @@ public:
struct PoolObjectNode : BasePoolObjectNode
{
amuse::ObjToken<T> m_obj;
PoolObjectNode(INode* parent, int row, ID id, amuse::ObjToken<T> obj)
: BasePoolObjectNode(parent, row, id, ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {}
PoolObjectNode(const QString& name, amuse::ObjToken<T> obj) : BasePoolObjectNode(name), m_obj(obj) {}
PoolObjectNode(ID id, amuse::ObjToken<T> obj)
: BasePoolObjectNode(id, ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {}
Type type() const { return TP; }
@ -394,20 +391,44 @@ public:
INode* node(const QModelIndex& index) const;
GroupNode* getGroupNode(INode* node) const;
bool canEdit(const QModelIndex& index) const;
void _undoDel(const QModelIndex& index, amuse::ObjToken<ProjectModel::INode> node, const NameUndoRegistry& nameReg);
amuse::ObjToken<ProjectModel::INode> _redoDel(const QModelIndex& index, NameUndoRegistry& nameReg);
void del(const QModelIndex& index);
RootNode* rootNode() const { return m_root.get(); }
GroupNode* newSubproject(const QString& name, UIMessenger& messenger);
SoundGroupNode* newSoundGroup(GroupNode* group, const QString& name, UIMessenger& messenger);
SongGroupNode* newSongGroup(GroupNode* group, const QString& name, UIMessenger& messenger);
SoundMacroNode* newSoundMacro(GroupNode* group, const QString& name, UIMessenger& messenger,
void _postAddNode(INode* n, const NameUndoRegistry& registry);
void _preDelNode(INode* n, NameUndoRegistry& registry);
void _addNode(GroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(GroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
GroupNode* newSubproject(const QString& name);
template <class NT, class T>
void _addGroupNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
template <class NT, class T>
void _delGroupNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
void _addNode(SoundGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SoundGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
SoundGroupNode* newSoundGroup(GroupNode* group, const QString& name);
void _addNode(SongGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SongGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
SongGroupNode* newSongGroup(GroupNode* group, const QString& name);
template <class NT, class T>
void _addPoolNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
template <class NT, class T>
void _delPoolNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
void _addNode(SoundMacroNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SoundMacroNode* node, GroupNode* parent, NameUndoRegistry& registry);
SoundMacroNode* newSoundMacro(GroupNode* group, const QString& name,
const SoundMacroTemplateEntry* templ = nullptr);
ADSRNode* newADSR(GroupNode* group, const QString& name, UIMessenger& messenger);
CurveNode* newCurve(GroupNode* group, const QString& name, UIMessenger& messenger);
KeymapNode* newKeymap(GroupNode* group, const QString& name, UIMessenger& messenger);
LayersNode* newLayers(GroupNode* group, const QString& name, UIMessenger& messenger);
void _addNode(ADSRNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(ADSRNode* node, GroupNode* parent, NameUndoRegistry& registry);
ADSRNode* newADSR(GroupNode* group, const QString& name);
void _addNode(CurveNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(CurveNode* node, GroupNode* parent, NameUndoRegistry& registry);
CurveNode* newCurve(GroupNode* group, const QString& name);
void _addNode(KeymapNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(KeymapNode* node, GroupNode* parent, NameUndoRegistry& registry);
KeymapNode* newKeymap(GroupNode* group, const QString& name);
void _addNode(LayersNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(LayersNode* node, GroupNode* parent, NameUndoRegistry& registry);
LayersNode* newLayers(GroupNode* group, const QString& name);
void del(const QModelIndex& index);
const QDir& dir() const { return m_dir; }
QString path() const { return m_dir.path(); }

View File

@ -281,157 +281,162 @@
<name>MainWindow</name>
<message>
<location filename="../MainWindow.ui" line="14"/>
<location filename="../MainWindow.cpp" line="186"/>
<location filename="../MainWindow.cpp" line="194"/>
<source>Amuse</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="268"/>
<location filename="../MainWindow.ui" line="70"/>
<source>Filter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="296"/>
<source>&amp;File</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="275"/>
<location filename="../MainWindow.ui" line="303"/>
<source>Recent &amp;Projects</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="291"/>
<location filename="../MainWindow.ui" line="319"/>
<source>Pro&amp;ject</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="306"/>
<location filename="../MainWindow.ui" line="334"/>
<source>&amp;Audio</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="313"/>
<location filename="../MainWindow.ui" line="341"/>
<source>&amp;MIDI</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="320"/>
<location filename="../MainWindow.ui" line="348"/>
<source>&amp;Edit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="336"/>
<location filename="../MainWindow.ui" line="364"/>
<source>&amp;New Project</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="341"/>
<location filename="../MainWindow.ui" line="369"/>
<source>&amp;Open Project</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="349"/>
<location filename="../MainWindow.ui" line="377"/>
<source>&amp;Cut</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="357"/>
<location filename="../MainWindow.ui" line="385"/>
<source>C&amp;opy</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="365"/>
<location filename="../MainWindow.ui" line="393"/>
<source>&amp;Paste</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="373"/>
<location filename="../MainWindow.ui" line="401"/>
<source>&amp;Delete</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="378"/>
<location filename="../MainWindow.ui" line="406"/>
<source>&amp;Import Groups</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="381"/>
<location filename="../MainWindow.ui" line="409"/>
<source>Ctrl+I</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="393"/>
<location filename="../MainWindow.ui" line="421"/>
<source>New SF&amp;X Group</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="405"/>
<location filename="../MainWindow.ui" line="433"/>
<source>New Son&amp;g Group</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="417"/>
<location filename="../MainWindow.ui" line="445"/>
<source>New Sound &amp;Macro</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="429"/>
<location filename="../MainWindow.ui" line="457"/>
<source>New &amp;Keymap</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="441"/>
<location filename="../MainWindow.ui" line="469"/>
<source>New &amp;Layers</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="449"/>
<location filename="../MainWindow.ui" line="477"/>
<source>&amp;Output Device:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="457"/>
<location filename="../MainWindow.ui" line="485"/>
<source>&amp;Input Device:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="465"/>
<location filename="../MainWindow.ui" line="493"/>
<source>&amp;Export GameCube Groups</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="468"/>
<location filename="../MainWindow.ui" line="496"/>
<source>Ctrl+E</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="480"/>
<location filename="../MainWindow.ui" line="508"/>
<source>&amp;New Subproject</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="492"/>
<location filename="../MainWindow.ui" line="520"/>
<source>New &amp;ADSR</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="504"/>
<location filename="../MainWindow.ui" line="532"/>
<source>New &amp;Curve</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="512"/>
<location filename="../MainWindow.ui" line="540"/>
<source>&amp;Save Project</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="520"/>
<location filename="../MainWindow.ui" line="548"/>
<source>&amp;Revert Project</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="528"/>
<location filename="../MainWindow.ui" line="556"/>
<source>Reload Sample &amp;Data</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.ui" line="536"/>
<location filename="../MainWindow.ui" line="564"/>
<source>I&amp;mport Songs</source>
<translation type="unfinished"></translation>
</message>
@ -471,269 +476,269 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="71"/>
<location filename="../MainWindow.cpp" line="79"/>
<source>Clear Recent Projects</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="77"/>
<location filename="../MainWindow.cpp" line="85"/>
<source>Quit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="194"/>
<location filename="../MainWindow.cpp" line="202"/>
<source>Amuse [%1/%2/%3]</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="199"/>
<location filename="../MainWindow.cpp" line="207"/>
<source>Amuse [%1]</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="230"/>
<location filename="../MainWindow.cpp" line="628"/>
<location filename="../MainWindow.cpp" line="238"/>
<location filename="../MainWindow.cpp" line="637"/>
<source>The directory at &apos;%1&apos; must not be empty.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="231"/>
<location filename="../MainWindow.cpp" line="629"/>
<location filename="../MainWindow.cpp" line="239"/>
<location filename="../MainWindow.cpp" line="638"/>
<source>Directory empty</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="236"/>
<location filename="../MainWindow.cpp" line="244"/>
<source>The directory at &apos;%1&apos; must exist for the Amuse editor.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="237"/>
<location filename="../MainWindow.cpp" line="245"/>
<source>Directory does not exist</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="240"/>
<location filename="../MainWindow.cpp" line="248"/>
<source>__amuse_test__</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="245"/>
<location filename="../MainWindow.cpp" line="253"/>
<source>The directory at &apos;%1&apos; must be writable for the Amuse editor: %2</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="247"/>
<location filename="../MainWindow.cpp" line="255"/>
<source>Unable to write to directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="295"/>
<location filename="../MainWindow.cpp" line="304"/>
<source>No Audio Devices Found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="318"/>
<location filename="../MainWindow.cpp" line="327"/>
<source>No MIDI Devices Found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="374"/>
<location filename="../MainWindow.cpp" line="383"/>
<source>SUSTAIN</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="611"/>
<location filename="../MainWindow.cpp" line="620"/>
<source>New Project</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="634"/>
<location filename="../MainWindow.cpp" line="643"/>
<source>The directory at &apos;%1&apos; does not exist.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="635"/>
<location filename="../MainWindow.cpp" line="644"/>
<source>Bad Directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="650"/>
<location filename="../MainWindow.cpp" line="659"/>
<source>Opening</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="650"/>
<location filename="../MainWindow.cpp" line="729"/>
<location filename="../MainWindow.cpp" line="813"/>
<location filename="../MainWindow.cpp" line="858"/>
<location filename="../MainWindow.cpp" line="659"/>
<location filename="../MainWindow.cpp" line="738"/>
<location filename="../MainWindow.cpp" line="822"/>
<location filename="../MainWindow.cpp" line="867"/>
<source>Scanning Project</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="662"/>
<location filename="../MainWindow.cpp" line="671"/>
<source>Opening %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="675"/>
<location filename="../MainWindow.cpp" line="684"/>
<source>Open Project</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="729"/>
<location filename="../MainWindow.cpp" line="738"/>
<source>Reloading Samples</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="741"/>
<location filename="../MainWindow.cpp" line="750"/>
<source>Scanning %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="751"/>
<location filename="../MainWindow.cpp" line="760"/>
<source>Import Project</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="761"/>
<location filename="../MainWindow.cpp" line="770"/>
<source>The file at &apos;%1&apos; could not be interpreted as a MusyX container.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="762"/>
<location filename="../MainWindow.cpp" line="771"/>
<source>Unsupported MusyX Container</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="767"/>
<location filename="../MainWindow.cpp" line="776"/>
<source>Sample Import Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="768"/>
<location filename="../MainWindow.cpp" line="777"/>
<source>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.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="772"/>
<location filename="../MainWindow.cpp" line="781"/>
<source>Import Compressed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="772"/>
<location filename="../MainWindow.cpp" line="781"/>
<source>Import WAVs</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="772"/>
<location filename="../MainWindow.cpp" line="781"/>
<source>Import Both</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="788"/>
<location filename="../MainWindow.cpp" line="797"/>
<source>Raw Import Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="789"/>
<location filename="../MainWindow.cpp" line="798"/>
<source>Would you like to scan for all MusyX group files in this directory?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="799"/>
<location filename="../MainWindow.cpp" line="808"/>
<source>Project Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="799"/>
<location filename="../MainWindow.cpp" line="808"/>
<source>What should this project be named?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="813"/>
<location filename="../MainWindow.cpp" line="858"/>
<location filename="../MainWindow.cpp" line="822"/>
<location filename="../MainWindow.cpp" line="867"/>
<source>Importing</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="825"/>
<location filename="../MainWindow.cpp" line="867"/>
<location filename="../MainWindow.cpp" line="834"/>
<location filename="../MainWindow.cpp" line="876"/>
<source>Importing %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="883"/>
<location filename="../MainWindow.cpp" line="892"/>
<source>Import Songs</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="984"/>
<location filename="../MainWindow.cpp" line="994"/>
<source>New Subproject</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1002"/>
<location filename="../MainWindow.cpp" line="1010"/>
<source>New SFX Group</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1003"/>
<location filename="../MainWindow.cpp" line="1011"/>
<source>What should the new SFX group in %1 be named?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1024"/>
<location filename="../MainWindow.cpp" line="1029"/>
<source>New Song Group</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1025"/>
<location filename="../MainWindow.cpp" line="1030"/>
<source>What should the new Song group in %1 be named?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1072"/>
<location filename="../MainWindow.cpp" line="1071"/>
<source>New ADSR</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1073"/>
<location filename="../MainWindow.cpp" line="1072"/>
<source>What should the new ADSR in %1 be named?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1094"/>
<location filename="../MainWindow.cpp" line="1090"/>
<source>New Curve</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1095"/>
<location filename="../MainWindow.cpp" line="1091"/>
<source>What should the new Curve in %1 be named?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1116"/>
<location filename="../MainWindow.cpp" line="1109"/>
<source>New Keymap</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1117"/>
<location filename="../MainWindow.cpp" line="1110"/>
<source>What should the new Keymap in %1 be named?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1138"/>
<location filename="../MainWindow.cpp" line="1128"/>
<source>New Layers</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1139"/>
<location filename="../MainWindow.cpp" line="1129"/>
<source>What should the new Layers in %1 be named?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="985"/>
<location filename="../MainWindow.cpp" line="995"/>
<source>What should this subproject be named?</source>
<translation type="unfinished"></translation>
</message>
@ -890,17 +895,17 @@
<context>
<name>PageObjectProxyModel</name>
<message>
<location filename="../ProjectModel.cpp" line="235"/>
<location filename="../ProjectModel.cpp" line="239"/>
<source>SoundMacros:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="237"/>
<location filename="../ProjectModel.cpp" line="241"/>
<source>Keymaps:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="239"/>
<location filename="../ProjectModel.cpp" line="243"/>
<source>Layers:</source>
<translation type="unfinished"></translation>
</message>
@ -924,116 +929,191 @@
<context>
<name>ProjectModel</name>
<message>
<location filename="../ProjectModel.cpp" line="521"/>
<location filename="../ProjectModel.cpp" line="519"/>
<source>Sound Macros</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="540"/>
<location filename="../ProjectModel.cpp" line="538"/>
<source>ADSRs</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="551"/>
<location filename="../ProjectModel.cpp" line="549"/>
<source>Curves</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="563"/>
<location filename="../ProjectModel.cpp" line="561"/>
<source>Keymaps</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="570"/>
<location filename="../ProjectModel.cpp" line="568"/>
<source>Layers</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="577"/>
<location filename="../ProjectModel.cpp" line="575"/>
<source>Samples</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="744"/>
<source>Delete %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="795"/>
<location filename="../ProjectModel.cpp" line="840"/>
<source>Subproject Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="795"/>
<location filename="../ProjectModel.cpp" line="840"/>
<source>The subproject %1 is already defined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="821"/>
<location filename="../ProjectModel.cpp" line="845"/>
<source>Add Subproject %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="895"/>
<source>Sound Group Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="821"/>
<location filename="../ProjectModel.cpp" line="840"/>
<location filename="../ProjectModel.cpp" line="895"/>
<location filename="../ProjectModel.cpp" line="920"/>
<source>The group %1 is already defined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="840"/>
<location filename="../ProjectModel.cpp" line="899"/>
<source>Add Sound Group %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="920"/>
<source>Song Group Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="860"/>
<location filename="../ProjectModel.cpp" line="924"/>
<source>Add Song Group %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1005"/>
<source>Sound Macro Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="860"/>
<location filename="../ProjectModel.cpp" line="1005"/>
<source>The macro %1 is already defined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="887"/>
<location filename="../ProjectModel.cpp" line="1015"/>
<source>Add Sound Macro %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1036"/>
<source>ADSR Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="887"/>
<location filename="../ProjectModel.cpp" line="1036"/>
<source>The ADSR %1 is already defined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="910"/>
<location filename="../ProjectModel.cpp" line="1042"/>
<source>Add ADSR %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1063"/>
<source>Curve Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="910"/>
<location filename="../ProjectModel.cpp" line="1063"/>
<source>The Curve %1 is already defined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="933"/>
<location filename="../ProjectModel.cpp" line="1069"/>
<source>Add Curve %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1090"/>
<source>Keymap Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="933"/>
<location filename="../ProjectModel.cpp" line="1090"/>
<source>The Keymap %1 is already defined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="955"/>
<location filename="../ProjectModel.cpp" line="1095"/>
<source>Add Keymap %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1116"/>
<source>Layers Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="955"/>
<location filename="../ProjectModel.cpp" line="1116"/>
<source>Layers %1 is already defined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1121"/>
<source>Add Layers %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1135"/>
<source>Delete Subproject %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1138"/>
<source>Delete SongGroup %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1141"/>
<source>Delete SFXGroup %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1144"/>
<source>Delete SoundMacro %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1147"/>
<source>Delete ADSR %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1150"/>
<source>Delete Curve %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1153"/>
<source>Delete Keymap %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1156"/>
<source>Delete Layers %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SFXModel</name>