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

View File

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

View File

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

View File

@ -169,7 +169,11 @@ QModelIndex PageObjectProxyModel::parent(const QModelIndex& child) const
int PageObjectProxyModel::rowCount(const QModelIndex& parent) 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(); auto tp = node->type();
if (tp != ProjectModel::INode::Type::Group) if (tp != ProjectModel::INode::Type::Group)
return static_cast<ProjectModel*>(sourceModel())->rowCount(parent); return static_cast<ProjectModel*>(sourceModel())->rowCount(parent);
@ -265,12 +269,34 @@ Qt::ItemFlags PageObjectProxyModel::flags(const QModelIndex& proxyIndex) const
return Qt::ItemIsEnabled | Qt::ItemIsSelectable; 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); auto nullNode = amuse::MakeObj<NullNode>(this);
m_nullChild = nullNode.get(); 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 ProjectModel::CollectionNode* ProjectModel::GroupNode::getCollectionOfType(Type tp) const
{ {
for (auto it = m_children.rbegin(); it != m_children.rend(); ++it) 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"); 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() bool ProjectModel::clearProjectData()
{ {
m_projectDatabase = amuse::ProjectDatabase(); m_projectDatabase = amuse::ProjectDatabase();
m_groups.clear(); m_groups.clear();
m_sorted.clear();
m_midiFiles.clear(); m_midiFiles.clear();
m_needsReset = true; m_needsReset = true;
@ -518,7 +516,7 @@ void ProjectModel::_buildGroupNode(GroupNode& gn)
gn.makeChild<SoundGroupNode>(grp.first, grp.second.get()); gn.makeChild<SoundGroupNode>(grp.first, grp.second.get());
{ {
CollectionNode& col = 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()); col.reserve(soundMacros.size());
for (const auto& macro : SortUnorderedMap(soundMacros)) for (const auto& macro : SortUnorderedMap(soundMacros))
col.makeChild<SoundMacroNode>(macro.first, macro.second.get()); col.makeChild<SoundMacroNode>(macro.first, macro.second.get());
@ -537,7 +535,7 @@ void ProjectModel::_buildGroupNode(GroupNode& gn)
} }
{ {
CollectionNode& col = 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); col.reserve(ADSRCount);
for (auto& t : tablesSort) for (auto& t : tablesSort)
{ {
@ -548,7 +546,7 @@ void ProjectModel::_buildGroupNode(GroupNode& gn)
} }
{ {
CollectionNode& col = 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); col.reserve(curveCount);
for (auto& t : tablesSort) for (auto& t : tablesSort)
{ {
@ -560,21 +558,21 @@ void ProjectModel::_buildGroupNode(GroupNode& gn)
} }
{ {
CollectionNode& col = 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()); col.reserve(keymaps.size());
for (auto& keymap : SortUnorderedMap(keymaps)) for (auto& keymap : SortUnorderedMap(keymaps))
col.makeChild<KeymapNode>(keymap.first, keymap.second.get()); col.makeChild<KeymapNode>(keymap.first, keymap.second.get());
} }
{ {
CollectionNode& col = 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()); col.reserve(layers.size());
for (auto& keymap : SortUnorderedMap(layers)) for (auto& keymap : SortUnorderedMap(layers))
col.makeChild<LayersNode>(keymap.first, keymap.second.get()); col.makeChild<LayersNode>(keymap.first, keymap.second.get());
} }
{ {
CollectionNode& col = 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()); col.reserve(samples.size());
for (auto& sample : SortUnorderedMap(samples)) for (auto& sample : SortUnorderedMap(samples))
col.makeChild<SampleNode>(sample.first, sample.second.get()); col.makeChild<SampleNode>(sample.first, sample.second.get());
@ -584,14 +582,13 @@ void ProjectModel::_buildGroupNode(GroupNode& gn)
void ProjectModel::_resetModelData() void ProjectModel::_resetModelData()
{ {
beginResetModel(); beginResetModel();
_buildSortedList();
m_projectDatabase.setIdDatabases(); m_projectDatabase.setIdDatabases();
m_root = amuse::MakeObj<RootNode>(); m_root = amuse::MakeObj<RootNode>();
m_root->reserve(m_sorted.size()); m_root->reserve(m_groups.size());
for (auto it = m_sorted.begin() ; it != m_sorted.end() ; ++it) for (auto it = m_groups.begin(); it != m_groups.end(); ++it)
{ {
it->m_it->second.setIdDatabases(); it->second.setIdDatabases();
GroupNode& gn = m_root->makeChild<GroupNode>(it->m_it); GroupNode& gn = m_root->makeChild<GroupNode>(it);
_buildGroupNode(gn); _buildGroupNode(gn);
} }
endResetModel(); endResetModel();
@ -623,9 +620,14 @@ QModelIndex ProjectModel::index(int row, int column, const QModelIndex& parent)
{ {
INode* parentItem; INode* parentItem;
if (!parent.isValid()) if (!parent.isValid())
{
parentItem = m_root.get(); parentItem = m_root.get();
}
else else
{
assert(parent.model() == this && "Not ProjectModel");
parentItem = static_cast<INode*>(parent.internalPointer()); parentItem = static_cast<INode*>(parent.internalPointer());
}
INode* childItem = parentItem->nullChild(); INode* childItem = parentItem->nullChild();
return createIndex(childItem->row(), column, childItem); return createIndex(childItem->row(), column, childItem);
@ -636,9 +638,14 @@ QModelIndex ProjectModel::index(int row, int column, const QModelIndex& parent)
INode* parentItem; INode* parentItem;
if (!parent.isValid()) if (!parent.isValid())
{
parentItem = m_root.get(); parentItem = m_root.get();
}
else else
{
assert(parent.model() == this && "Not ProjectModel");
parentItem = static_cast<INode*>(parent.internalPointer()); parentItem = static_cast<INode*>(parent.internalPointer());
}
INode* childItem = parentItem->child(row); INode* childItem = parentItem->child(row);
if (childItem) if (childItem)
@ -658,6 +665,7 @@ QModelIndex ProjectModel::parent(const QModelIndex& index) const
{ {
if (!index.isValid()) if (!index.isValid())
return QModelIndex(); return QModelIndex();
assert(index.model() == this && "Not ProjectModel");
INode* childItem = static_cast<INode*>(index.internalPointer()); INode* childItem = static_cast<INode*>(index.internalPointer());
INode* parentItem = childItem->parent(); INode* parentItem = childItem->parent();
@ -689,6 +697,7 @@ QVariant ProjectModel::data(const QModelIndex& index, int role) const
{ {
if (!index.isValid()) if (!index.isValid())
return QVariant(); return QVariant();
assert(index.model() == this && "Not ProjectModel");
INode* item = static_cast<INode*>(index.internalPointer()); INode* item = static_cast<INode*>(index.internalPointer());
@ -707,6 +716,7 @@ Qt::ItemFlags ProjectModel::flags(const QModelIndex& index) const
{ {
if (!index.isValid()) if (!index.isValid())
return Qt::NoItemFlags; return Qt::NoItemFlags;
assert(index.model() == this && "Not ProjectModel");
return static_cast<INode*>(index.internalPointer())->flags(); return static_cast<INode*>(index.internalPointer())->flags();
} }
@ -715,6 +725,7 @@ ProjectModel::INode* ProjectModel::node(const QModelIndex& index) const
{ {
if (!index.isValid()) if (!index.isValid())
return m_root.get(); return m_root.get();
assert(index.model() == this && "Not ProjectModel");
return static_cast<INode*>(index.internalPointer()); return static_cast<INode*>(index.internalPointer());
} }
@ -731,241 +742,424 @@ bool ProjectModel::canEdit(const QModelIndex& index) const
{ {
if (!index.isValid()) if (!index.isValid())
return false; return false;
assert(index.model() == this && "Not ProjectModel");
return (static_cast<INode*>(index.internalPointer())->flags() & Qt::ItemIsSelectable) != Qt::NoItemFlags; 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; setIdDatabases(n);
amuse::ObjToken<ProjectModel::INode> m_node; n->depthTraverse([&registry](INode* node)
ProjectModel::NameUndoRegistry m_nameReg;
public:
DeleteNodeUndoCommand(const QModelIndex& index)
: QUndoCommand(ProjectModel::tr("Delete %1").arg(index.data().toString())), m_deleteIdx(index) {}
void undo()
{ {
g_MainWindow->projectModel()->_undoDel(m_deleteIdx, std::move(m_node), m_nameReg); node->registerNames(registry);
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);
return true; 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(); 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) int idx = node->row();
{ beginRemoveRows(QModelIndex(), idx, idx);
g_MainWindow->aboutToDeleteNode(node); _preDelNode(node, registry);
node->unregisterNames(nameReg); m_groups.erase(node->m_it);
return true; node->m_it = {};
}); m_root->removeChild(node);
beginRemoveRows(index.parent(), index.row(), index.row());
amuse::ObjToken<ProjectModel::INode> ret = node(index.parent())->removeChild(index.row());
endRemoveRows(); 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) void ProjectModel::del(const QModelIndex& index)
{ {
if (!index.isValid()) if (!index.isValid())
return; return;
g_MainWindow->pushUndoCommand(new DeleteNodeUndoCommand(index)); assert(index.model() == this && "Not ProjectModel");
} INode* n = node(index);
QUndoCommand* cmd = nullptr;
ProjectModel::GroupNode* ProjectModel::newSubproject(const QString& name, UIMessenger& messenger) switch (n->type())
{
if (m_groups.find(name) != m_groups.cend())
{ {
messenger.critical(tr("Subproject Conflict"), tr("The subproject %1 is already defined").arg(name)); case INode::Type::Group:
return nullptr; 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 (cmd)
if (!MkPath(dir.path(), messenger)) g_MainWindow->pushUndoCommand(cmd);
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;
} }
ProjectModel::GroupNode* ProjectModel::getGroupOfSfx(amuse::SFXId id) const ProjectModel::GroupNode* ProjectModel::getGroupOfSfx(amuse::SFXId id) const

View File

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

View File

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