Bug fixes, xref search, more context menus

This commit is contained in:
Jack Andersen 2018-08-25 18:57:02 -10:00
parent 27cdee0c14
commit 19c5443e9e
17 changed files with 922 additions and 294 deletions

View File

@ -278,3 +278,47 @@ ListingDeleteButton::ListingDeleteButton(QWidget* parent)
setToolTip(tr("Delete this SoundMacro"));
setIcon(ListingDeleteIcon);
}
bool BaseObjectDelegate::editorEvent(QEvent *event, QAbstractItemModel* model,
const QStyleOptionViewItem &option, const QModelIndex &index)
{
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* ev = static_cast<QMouseEvent*>(event);
if (ev->button() == Qt::RightButton)
{
ProjectModel::INode* node = getNode(model, index);
ContextMenu* menu = new ContextMenu;
QAction* openEditorAction = new QAction(tr("Open in Editor"), menu);
openEditorAction->setData(QVariant::fromValue((void*)node));
openEditorAction->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
connect(openEditorAction, SIGNAL(triggered()), this, SLOT(doOpenEditor()));
menu->addAction(openEditorAction);
QAction* findUsagesAction = new QAction(tr("Find Usages"), menu);
findUsagesAction->setData(QVariant::fromValue((void*)node));
findUsagesAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-find")));
connect(findUsagesAction, SIGNAL(triggered()), this, SLOT(doFindUsages()));
menu->addAction(findUsagesAction);
menu->popup(ev->globalPos());
}
}
return false;
}
void BaseObjectDelegate::doOpenEditor()
{
QAction* act = static_cast<QAction*>(sender());
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
g_MainWindow->openEditor(node);
}
void BaseObjectDelegate::doFindUsages()
{
QAction* act = static_cast<QAction*>(sender());
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
g_MainWindow->findUsages(node);
}

View File

@ -12,6 +12,8 @@
#include <QAction>
#include <QPushButton>
#include <QLabel>
#include <QMenu>
#include <QStyledItemDelegate>
#include "ProjectModel.hpp"
class EditorWidget : public QWidget
@ -225,4 +227,28 @@ public:
void leaveEvent(QEvent* event);
};
class ContextMenu : public QMenu
{
public:
void hideEvent(QHideEvent* ev)
{
QMenu::hideEvent(ev);
deleteLater();
}
};
class BaseObjectDelegate : public QStyledItemDelegate
{
Q_OBJECT
protected:
virtual ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const = 0;
public:
explicit BaseObjectDelegate(QObject* parent = Q_NULLPTR) : QStyledItemDelegate(parent) {}
bool editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option, const QModelIndex &index);
private slots:
void doOpenEditor();
void doFindUsages();
};
#endif //AMUSE_EDITOR_WIDGET_HPP

View File

@ -98,7 +98,16 @@ public:
};
SoundMacroDelegate::SoundMacroDelegate(QObject* parent)
: QStyledItemDelegate(parent) {}
: BaseObjectDelegate(parent) {}
ProjectModel::INode* SoundMacroDelegate::getNode(const QAbstractItemModel* __model, const QModelIndex& index) const
{
const LayersModel* model = static_cast<const LayersModel*>(__model);
const amuse::LayerMapping& layer = (*model->m_node->m_obj)[index.row()];
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
ProjectModel::CollectionNode* smColl = group->getCollectionOfType(ProjectModel::INode::Type::SoundMacro);
return smColl->nodeOfId(layer.macro.id);
}
QWidget* SoundMacroDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{

View File

@ -8,9 +8,11 @@
#include <QToolButton>
#include <QStyledItemDelegate>
class SoundMacroDelegate : public QStyledItemDelegate
class SoundMacroDelegate : public BaseObjectDelegate
{
Q_OBJECT
protected:
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
public:
explicit SoundMacroDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;

View File

@ -7,7 +7,10 @@ MIDIReader::MIDIReader(amuse::Engine& engine, bool useLock)
void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->keyOff(chan, key, velocity);
return;
}
auto keySearch = m_chanVoxs.find(key);
if (keySearch == m_chanVoxs.cend())
@ -23,7 +26,10 @@ void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
void MIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->keyOn(chan, key, velocity);
return;
}
if (m_lastVoice && m_lastVoice->isDestroyed())
m_lastVoice.reset();
@ -65,7 +71,10 @@ void MIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*press
void MIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->setCtrlValue(chan, control, value);
return;
}
if (control == 1)
{
@ -103,7 +112,10 @@ void MIDIReader::pitchBend(uint8_t chan, int16_t pitch)
void MIDIReader::allSoundOff(uint8_t chan)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->kill();
return;
}
for (auto& v : m_engine.getActiveVoices())
v->kill();
@ -116,7 +128,10 @@ void MIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) {}
void MIDIReader::allNotesOff(uint8_t chan)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->kill();
return;
}
for (auto& v : m_engine.getActiveVoices())
v->kill();

View File

@ -25,7 +25,6 @@ MainWindow::MainWindow(QWidget* parent)
m_navIt(m_navList.begin()),
m_treeDelegate(*this, this),
m_mainMessenger(this),
m_filterProjectModel(this),
m_undoStack(new QUndoStack(this)),
m_backgroundThread(this)
{
@ -36,10 +35,6 @@ MainWindow::MainWindow(QWidget* parent)
QPalette palette = m_ui.projectOutlineFilter->palette();
palette.setColor(QPalette::Base, palette.color(QPalette::Background));
m_ui.projectOutlineFilter->setPalette(palette);
connect(m_ui.projectOutlineFilter, SIGNAL(textChanged(const QString&)),
&m_filterProjectModel, SLOT(setFilterRegExp(const QString&)));
m_filterProjectModel.setRecursiveFilteringEnabled(true);
m_filterProjectModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
m_ui.projectOutline->setItemDelegate(&m_treeDelegate);
connect(m_ui.projectOutline, SIGNAL(activated(const QModelIndex&)),
this, SLOT(outlineItemActivated(const QModelIndex&)));
@ -286,8 +281,10 @@ bool MainWindow::setProjectPath(const QString& path)
if (m_projectModel)
m_projectModel->deleteLater();
m_projectModel = new ProjectModel(path, this);
m_filterProjectModel.setSourceModel(m_projectModel);
m_ui.projectOutline->setModel(&m_filterProjectModel);
m_ui.projectOutlineFilter->clear();
connect(m_ui.projectOutlineFilter, SIGNAL(textChanged(const QString&)),
m_projectModel->getOutlineProxy(), SLOT(setFilterRegExp(const QString&)));
m_ui.projectOutline->setModel(m_projectModel->getOutlineProxy());
connect(m_ui.projectOutline->selectionModel(),
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
this, SLOT(onOutlineSelectionChanged(const QItemSelection&, const QItemSelection&)));
@ -1165,8 +1162,25 @@ bool TreeDelegate::editorEvent(QEvent* event,
QMouseEvent* ev = static_cast<QMouseEvent*>(event);
if (ev->button() == Qt::RightButton)
{
m_window.m_ui.projectOutline->setCurrentIndex(index);
QMenu* menu = new QMenu(g_MainWindow->m_ui.projectOutline);
ContextMenu* menu = new ContextMenu;
if (node->type() == ProjectModel::INode::Type::Group)
{
QAction* exportGroupAction = new QAction(tr("Export GameCube Group"), menu);
exportGroupAction->setData(QVariant::fromValue((void*)node));
connect(exportGroupAction, SIGNAL(triggered()), this, SLOT(doExportGroup()));
menu->addAction(exportGroupAction);
menu->addSeparator();
}
QAction* findUsagesAction = new QAction(tr("Find Usages"), menu);
findUsagesAction->setData(QVariant::fromValue((void*)node));
findUsagesAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-find")));
connect(findUsagesAction, SIGNAL(triggered()), this, SLOT(doFindUsages()));
menu->addAction(findUsagesAction);
menu->addSeparator();
QAction* cutAction = new QAction(tr("Cut"), menu);
cutAction->setData(index);
@ -1207,24 +1221,56 @@ bool TreeDelegate::editorEvent(QEvent* event,
QAction* renameAction = new QAction(tr("Rename"), menu);
renameAction->setData(index);
renameAction->setShortcut(tr("F2"));
renameAction->setShortcut(Qt::Key_F2);
connect(renameAction, SIGNAL(triggered()), this, SLOT(doRename()));
menu->addAction(renameAction);
menu->popup(ev->globalPos());
return true;
}
}
return false;
}
void TreeDelegate::doExportGroup()
{
if (!m_window.m_projectModel)
return;
QAction* act = qobject_cast<QAction*>(sender());
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
{
if (node->type() != ProjectModel::INode::Type::Group)
return;
QString groupName = static_cast<ProjectModel::GroupNode*>(node)->name();
ProjectModel* model = m_window.m_projectModel;
QFileInfo dirInfo(model->dir(), QStringLiteral("out"));
if (!MkPath(dirInfo.filePath(), m_window.m_mainMessenger))
return;
QDir dir(dirInfo.filePath());
m_window.startBackgroundTask(BackgroundTaskId::TaskExport, tr("Exporting"), tr("Exporting %1").arg(groupName),
[model, dir, groupName](BackgroundTask& task)
{
model->exportGroup(dir.path(), groupName, task.uiMessenger());
});
}
}
void TreeDelegate::doFindUsages()
{
QAction* act = qobject_cast<QAction*>(sender());
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
m_window.findUsages(node);
}
void TreeDelegate::doCut()
{
QAction* act = qobject_cast<QAction*>(sender());
if (!m_window.m_projectModel)
return;
m_window.m_projectModel->cut(m_window.m_filterProjectModel.mapToSource(act->data().toModelIndex()));
m_window.m_projectModel->cut(m_window.m_projectModel->getOutlineProxy()->mapToSource(act->data().toModelIndex()));
}
void TreeDelegate::doCopy()
@ -1232,7 +1278,7 @@ void TreeDelegate::doCopy()
QAction* act = qobject_cast<QAction*>(sender());
if (!m_window.m_projectModel)
return;
m_window.m_projectModel->copy(m_window.m_filterProjectModel.mapToSource(act->data().toModelIndex()));
m_window.m_projectModel->copy(m_window.m_projectModel->getOutlineProxy()->mapToSource(act->data().toModelIndex()));
}
void TreeDelegate::doPaste()
@ -1240,7 +1286,7 @@ void TreeDelegate::doPaste()
QAction* act = qobject_cast<QAction*>(sender());
if (!m_window.m_projectModel)
return;
m_window.m_projectModel->paste(m_window.m_filterProjectModel.mapToSource(act->data().toModelIndex()));
m_window.m_projectModel->paste(m_window.m_projectModel->getOutlineProxy()->mapToSource(act->data().toModelIndex()));
}
void TreeDelegate::doDuplicate()
@ -1249,10 +1295,10 @@ void TreeDelegate::doDuplicate()
if (!m_window.m_projectModel)
return;
QModelIndex newIdx =
m_window.m_projectModel->duplicate(m_window.m_filterProjectModel.mapToSource(act->data().toModelIndex()));
m_window.m_projectModel->duplicate(m_window.m_projectModel->getOutlineProxy()->mapToSource(act->data().toModelIndex()));
if (newIdx.isValid())
{
newIdx = m_window.m_filterProjectModel.mapFromSource(newIdx);
newIdx = m_window.m_projectModel->getOutlineProxy()->mapFromSource(newIdx);
m_window.m_ui.projectOutline->edit(newIdx);
}
}
@ -1262,7 +1308,7 @@ void TreeDelegate::doDelete()
QAction* act = qobject_cast<QAction*>(sender());
if (!m_window.m_projectModel)
return;
m_window.m_projectModel->del(m_window.m_filterProjectModel.mapToSource(act->data().toModelIndex()));
m_window.m_projectModel->del(m_window.m_projectModel->getOutlineProxy()->mapToSource(act->data().toModelIndex()));
}
void TreeDelegate::doRename()
@ -1285,7 +1331,7 @@ ProjectModel::GroupNode* MainWindow::getSelectedGroupNode() const
if (!m_ui.projectOutline->currentIndex().isValid())
return nullptr;
return m_projectModel->getGroupNode(m_projectModel->node(
m_filterProjectModel.mapToSource(m_ui.projectOutline->currentIndex())));
m_projectModel->getOutlineProxy()->mapToSource(m_ui.projectOutline->currentIndex())));
}
QString MainWindow::getSelectedGroupName() const
@ -1304,7 +1350,7 @@ void MainWindow::_recursiveExpandOutline(const QModelIndex& filterIndex) const
void MainWindow::recursiveExpandAndSelectOutline(const QModelIndex& index) const
{
QModelIndex filterIndex = m_filterProjectModel.mapFromSource(index);
QModelIndex filterIndex = m_projectModel->getOutlineProxy()->mapFromSource(index);
_recursiveExpandOutline(filterIndex);
if (filterIndex.isValid())
m_ui.projectOutline->setCurrentIndex(filterIndex);
@ -1484,6 +1530,11 @@ void MainWindow::goBack()
updateNavigationButtons();
}
void MainWindow::findUsages(ProjectModel::INode* node)
{
m_ui.projectOutlineFilter->setText(QStringLiteral("usages:") + node->name());
}
void MainWindow::aboutToShowAudioIOMenu()
{
refreshAudioIO();
@ -1661,28 +1712,28 @@ void MainWindow::outlineCutAction()
{
if (!m_projectModel)
return;
m_projectModel->cut(m_filterProjectModel.mapToSource(m_ui.projectOutline->currentIndex()));
m_projectModel->cut(m_projectModel->getOutlineProxy()->mapToSource(m_ui.projectOutline->currentIndex()));
}
void MainWindow::outlineCopyAction()
{
if (!m_projectModel)
return;
m_projectModel->copy(m_filterProjectModel.mapToSource(m_ui.projectOutline->currentIndex()));
m_projectModel->copy(m_projectModel->getOutlineProxy()->mapToSource(m_ui.projectOutline->currentIndex()));
}
void MainWindow::outlinePasteAction()
{
if (!m_projectModel)
return;
m_projectModel->paste(m_filterProjectModel.mapToSource(m_ui.projectOutline->currentIndex()));
m_projectModel->paste(m_projectModel->getOutlineProxy()->mapToSource(m_ui.projectOutline->currentIndex()));
}
void MainWindow::outlineDeleteAction()
{
if (!m_projectModel)
return;
m_projectModel->del(m_filterProjectModel.mapToSource(m_ui.projectOutline->currentIndex()));
m_projectModel->del(m_projectModel->getOutlineProxy()->mapToSource(m_ui.projectOutline->currentIndex()));
}
void MainWindow::onFocusChanged(QWidget* old, QWidget* now)
@ -1761,7 +1812,7 @@ void MainWindow::onClipboardChanged()
void MainWindow::outlineItemActivated(const QModelIndex& index)
{
ProjectModel::INode* node = m_projectModel->node(m_filterProjectModel.mapToSource(index));
ProjectModel::INode* node = m_projectModel->node(m_projectModel->getOutlineProxy()->mapToSource(index));
if (!node)
return;
openEditor(node);
@ -1793,7 +1844,7 @@ AmuseItemEditFlags MainWindow::outlineEditFlags()
QModelIndex curIndex = m_ui.projectOutline->currentIndex();
if (!curIndex.isValid())
return AmuseItemNone;
return m_projectModel->editFlags(m_filterProjectModel.mapToSource(curIndex));
return m_projectModel->editFlags(m_projectModel->getOutlineProxy()->mapToSource(curIndex));
}
void MainWindow::onOutlineSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
@ -1804,7 +1855,7 @@ void MainWindow::onOutlineSelectionChanged(const QItemSelection& selected, const
if (selected.indexes().empty())
setItemEditFlags(AmuseItemNone);
else
setItemEditFlags(m_projectModel->editFlags(m_filterProjectModel.mapToSource(selected.indexes().front())));
setItemEditFlags(m_projectModel->editFlags(m_projectModel->getOutlineProxy()->mapToSource(selected.indexes().front())));
}
void MainWindow::onTextSelect()

View File

@ -79,6 +79,8 @@ public:
const QStyleOptionViewItem &option,
const QModelIndex &index);
public slots:
void doExportGroup();
void doFindUsages();
void doCut();
void doCopy();
void doPaste();
@ -104,7 +106,6 @@ class MainWindow : public QMainWindow
TreeDelegate m_treeDelegate;
UIMessenger m_mainMessenger;
ProjectModel* m_projectModel = nullptr;
QSortFilterProxyModel m_filterProjectModel;
QWidget* m_faceSvg;
SongGroupEditor* m_songGroupEditor = nullptr;
SoundGroupEditor* m_soundGroupEditor = nullptr;
@ -201,6 +202,7 @@ public:
void setItemNewEnabled(bool enabled);
AmuseItemEditFlags outlineEditFlags();
bool isUiDisabled() const { return m_uiDisabled; }
void findUsages(ProjectModel::INode* node);
public slots:
void newAction();

View File

@ -16,6 +16,165 @@ QIcon ProjectModel::GroupNode::Icon;
QIcon ProjectModel::SongGroupNode::Icon;
QIcon ProjectModel::SoundGroupNode::Icon;
OutlineFilterProxyModel::OutlineFilterProxyModel(ProjectModel* source)
: QSortFilterProxyModel(source), m_usageKey({}, Qt::CaseInsensitive)
{
setSourceModel(source);
setRecursiveFilteringEnabled(true);
setFilterCaseSensitivity(Qt::CaseInsensitive);
}
void OutlineFilterProxyModel::setFilterRegExp(const QString& pattern)
{
if (pattern.startsWith(QStringLiteral("usages:")))
{
m_usageKey.setPattern(pattern.mid(7));
QSortFilterProxyModel::setFilterRegExp(QString());
}
else
{
m_usageKey.setPattern(QString());
QSortFilterProxyModel::setFilterRegExp(pattern);
}
}
static void VisitObjectFields(ProjectModel::SongGroupNode* n,
const std::function<bool(amuse::ObjectId, amuse::NameDB*)>& func)
{
for (const auto& p : n->m_index->m_normPages)
if (!func(p.second.objId.id, nullptr))
return;
for (const auto& p : n->m_index->m_drumPages)
if (!func(p.second.objId.id, nullptr))
return;
}
static void VisitObjectFields(ProjectModel::SoundGroupNode* n,
const std::function<bool(amuse::ObjectId, amuse::NameDB*)>& func)
{
for (const auto& p : n->m_index->m_sfxEntries)
if (!func(p.second.objId.id, nullptr))
return;
}
static void VisitObjectFields(ProjectModel::SoundMacroNode* n,
const std::function<bool(amuse::ObjectId, amuse::NameDB*)>& func)
{
for (const auto& p : n->m_obj->m_cmds)
{
const amuse::SoundMacro::CmdIntrospection* in = amuse::SoundMacro::GetCmdIntrospection(p->Isa());
if (!in)
continue;
for (int i = 0; i < 7; ++i)
{
const auto& field = in->m_fields[i];
if (field.m_name.empty())
break;
switch (field.m_tp)
{
case amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroId:
if (!func(amuse::AccessField<amuse::SoundMacroIdDNA<athena::Little>>(p.get(), field).id, amuse::SoundMacroId::CurNameDB))
return;
break;
case amuse::SoundMacro::CmdIntrospection::Field::Type::TableId:
if (!func(amuse::AccessField<amuse::TableIdDNA<athena::Little>>(p.get(), field).id, amuse::TableId::CurNameDB))
return;
break;
case amuse::SoundMacro::CmdIntrospection::Field::Type::SampleId:
if (!func(amuse::AccessField<amuse::SampleIdDNA<athena::Little>>(p.get(), field).id, amuse::SampleId::CurNameDB))
return;
break;
default:
break;
}
}
}
}
static void VisitObjectFields(ProjectModel::KeymapNode* n,
const std::function<bool(amuse::ObjectId, amuse::NameDB*)>& func)
{
for (const auto& p : *n->m_obj)
if (!func(p.macro.id, amuse::SoundMacroId::CurNameDB))
return;
}
static void VisitObjectFields(ProjectModel::LayersNode* n,
const std::function<bool(amuse::ObjectId, amuse::NameDB*)>& func)
{
for (const auto& p : *n->m_obj)
if (!func(p.macro.id, amuse::SoundMacroId::CurNameDB))
return;
}
bool OutlineFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
if (m_usageKey.pattern().isEmpty())
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
ProjectModel* model = static_cast<ProjectModel*>(sourceModel());
ProjectModel::INode* node = model->node(model->index(sourceRow, 0, sourceParent));
ProjectModel::GroupNode* gn = model->getGroupNode(node);
if (!gn)
return true;
model->setIdDatabases(gn);
bool foundMatch = false;
auto visitor = [this, &foundMatch](amuse::ObjectId id, amuse::NameDB* db)
{
if (id.id == 0xffff)
return true;
std::string_view name;
if (!db)
{
if (id.id & 0x8000)
name = amuse::LayersId::CurNameDB->resolveNameFromId(id);
else if (id.id & 0x4000)
name = amuse::KeymapId::CurNameDB->resolveNameFromId(id);
else
name = amuse::SoundMacroId::CurNameDB->resolveNameFromId(id);
}
else
{
auto search = db->m_idToString.find(id);
if (search != db->m_idToString.cend())
name = search->second;
}
if (!name.empty() && QString::fromUtf8(name.data(), int(name.length())).contains(m_usageKey))
{
foundMatch = true;
return false;
}
return true;
};
switch (node->type())
{
case ProjectModel::INode::Type::SongGroup:
VisitObjectFields(static_cast<ProjectModel::SongGroupNode*>(node), visitor);
break;
case ProjectModel::INode::Type::SoundGroup:
VisitObjectFields(static_cast<ProjectModel::SoundGroupNode*>(node), visitor);
break;
case ProjectModel::INode::Type::SoundMacro:
VisitObjectFields(static_cast<ProjectModel::SoundMacroNode*>(node), visitor);
break;
case ProjectModel::INode::Type::Keymap:
VisitObjectFields(static_cast<ProjectModel::KeymapNode*>(node), visitor);
break;
case ProjectModel::INode::Type::Layer:
VisitObjectFields(static_cast<ProjectModel::LayersNode*>(node), visitor);
break;
default:
break;
}
return foundMatch;
}
NullItemProxyModel::NullItemProxyModel(ProjectModel* source)
: QIdentityProxyModel(source)
{
@ -407,7 +566,7 @@ ProjectModel::BasePoolObjectNode* ProjectModel::CollectionNode::nodeOfId(amuse::
}
ProjectModel::ProjectModel(const QString& path, QObject* parent)
: QAbstractItemModel(parent), m_dir(path), m_nullProxy(this), m_pageObjectProxy(this)
: QAbstractItemModel(parent), m_dir(path), m_outlineProxy(this), m_nullProxy(this), m_pageObjectProxy(this)
{
m_root = amuse::MakeObj<RootNode>();

View File

@ -3,6 +3,7 @@
#include <QAbstractItemModel>
#include <QIdentityProxyModel>
#include <QSortFilterProxyModel>
#include <QDir>
#include <QIcon>
#include <map>
@ -28,6 +29,17 @@ enum AmuseItemEditFlags
AmuseItemAll = (AmuseItemCut | AmuseItemCopy | AmuseItemPaste | AmuseItemDelete)
};
class OutlineFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
QRegExp m_usageKey;
public:
explicit OutlineFilterProxyModel(ProjectModel* source);
public slots:
void setFilterRegExp(const QString &pattern);
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
};
class NullItemProxyModel : public QIdentityProxyModel
{
Q_OBJECT
@ -82,6 +94,7 @@ public:
private:
QDir m_dir;
OutlineFilterProxyModel m_outlineProxy;
NullItemProxyModel m_nullProxy;
PageObjectProxyModel m_pageObjectProxy;
@ -484,6 +497,7 @@ public:
const QDir& dir() const { return m_dir; }
QString path() const { return m_dir.path(); }
OutlineFilterProxyModel* getOutlineProxy() { return &m_outlineProxy; }
NullItemProxyModel* getNullProxy() { return &m_nullProxy; }
PageObjectProxyModel* getPageObjectProxy() { return &m_pageObjectProxy; }

View File

@ -229,7 +229,17 @@ public:
};
PageObjectDelegate::PageObjectDelegate(QObject* parent)
: QStyledItemDelegate(parent) {}
: BaseObjectDelegate(parent) {}
ProjectModel::INode* PageObjectDelegate::getNode(const QAbstractItemModel* __model, const QModelIndex& index) const
{
const PageModel* model = static_cast<const PageModel*>(__model);
auto entry = model->m_sorted[index.row()];
if (entry->second.objId.id.id == 0xffff)
return nullptr;
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
return group->pageObjectNodeOfId(entry->second.objId.id);
}
QWidget* PageObjectDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
@ -242,12 +252,8 @@ QWidget* PageObjectDelegate::createEditor(QWidget* parent, const QStyleOptionVie
void PageObjectDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
const PageModel* model = static_cast<const PageModel*>(index.model());
auto entry = model->m_sorted[index.row()];
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
ProjectModel::BasePoolObjectNode* node = group->pageObjectNodeOfId(entry->second.objId.id);
int idx = 0;
if (node)
if (ProjectModel::BasePoolObjectNode* node = static_cast<ProjectModel::BasePoolObjectNode*>(getNode(index.model(), index)))
idx = g_MainWindow->projectModel()->getPageObjectProxy()->mapFromSource(g_MainWindow->projectModel()->index(node)).row();
static_cast<EditorFieldPageObjectNode*>(editor)->setCurrentIndex(idx);
if (static_cast<EditorFieldPageObjectNode*>(editor)->shouldPopupOpen())
@ -316,6 +322,14 @@ QWidget* MIDIFileDelegate::createEditor(QWidget* parent, const QStyleOptionViewI
return field;
}
void MIDIFileDelegate::destroyEditor(QWidget *editor, const QModelIndex &index) const
{
QTableView* table = static_cast<QTableView*>(editor->parentWidget()->parentWidget());
editor->deleteLater();
emit const_cast<QAbstractItemModel*>(index.model())->dataChanged(index, index);
table->setCurrentIndex(index);
}
void MIDIFileDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
MIDIFileFieldWidget* widget = static_cast<MIDIFileFieldWidget*>(editor);
@ -339,6 +353,152 @@ void MIDIFileDelegate::setModelData(QWidget* editor, QAbstractItemModel* m, cons
emit m->dataChanged(index, index);
}
bool MIDIFileDelegate::editorEvent(QEvent* event, QAbstractItemModel* model,
const QStyleOptionViewItem& option, const QModelIndex& index)
{
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* ev = static_cast<QMouseEvent*>(event);
if (ev->button() == Qt::RightButton)
{
QString path = index.data().toString();
if (path.isEmpty())
return false;
ContextMenu* menu = new ContextMenu;
QAction* midiAction = new QAction(tr("Save As MIDI"), menu);
midiAction->setData(path);
midiAction->setIcon(QIcon::fromTheme(QStringLiteral("file-save")));
connect(midiAction, SIGNAL(triggered()), this, SLOT(doExportMIDI()));
menu->addAction(midiAction);
QAction* sngAction = new QAction(tr("Save As SNG"), menu);
sngAction->setData(path);
sngAction->setIcon(QIcon::fromTheme(QStringLiteral("file-save")));
connect(sngAction, SIGNAL(triggered()), this, SLOT(doExportSNG()));
menu->addAction(sngAction);
menu->popup(ev->globalPos());
}
}
return false;
}
void MIDIFileDelegate::doExportMIDI()
{
QAction* act = static_cast<QAction*>(sender());
QString path = act->data().toString();
if (path.isEmpty())
return;
QString inPath = g_MainWindow->projectModel()->dir().absoluteFilePath(path);
std::vector<uint8_t> data;
{
QFile fi(inPath);
if (!fi.open(QFile::ReadOnly))
{
g_MainWindow->uiMessenger().critical(tr("File Error"),
tr("Unable to open %1 for reading: %1").arg(inPath).arg(fi.errorString()));
return;
}
auto d = fi.readAll();
data.resize(d.size());
memcpy(&data[0], d.data(), d.size());
}
if (!memcmp(data.data(), "MThd", 4))
{
//data = amuse::SongConverter::MIDIToSong(data, 1, true);
}
else
{
bool isBig;
if (amuse::SongState::DetectVersion(data.data(), isBig) < 0)
{
g_MainWindow->uiMessenger().critical(tr("File Error"),
tr("Invalid song data at %1").arg(inPath));
return;
}
int version;
data = amuse::SongConverter::SongToMIDI(data.data(), version, isBig);
}
QFileInfo inInfo(inPath);
QString outPath =
QFileDialog::getSaveFileName(g_MainWindow, tr("Export MIDI"),
QFileInfo(inInfo.path(), inInfo.completeBaseName() + QStringLiteral(".mid")).filePath(), tr("MIDI(*.mid)"));
if (outPath.isEmpty())
return;
QFile fo(outPath);
if (!fo.open(QFile::WriteOnly))
{
g_MainWindow->uiMessenger().critical(tr("File Error"),
tr("Unable to open %1 for writing: %1").arg(outPath).arg(fo.errorString()));
return;
}
fo.write((char*)data.data(), data.size());
}
void MIDIFileDelegate::doExportSNG()
{
QAction* act = static_cast<QAction*>(sender());
QString path = act->data().toString();
if (path.isEmpty())
return;
QString inPath = g_MainWindow->projectModel()->dir().absoluteFilePath(path);
std::vector<uint8_t> data;
{
QFile fi(inPath);
if (!fi.open(QFile::ReadOnly))
{
g_MainWindow->uiMessenger().critical(tr("File Error"),
tr("Unable to open %1 for reading: %1").arg(inPath).arg(fi.errorString()));
return;
}
auto d = fi.readAll();
data.resize(d.size());
memcpy(&data[0], d.data(), d.size());
}
if (!memcmp(data.data(), "MThd", 4))
{
data = amuse::SongConverter::MIDIToSong(data, 1, true);
if (data.empty())
{
g_MainWindow->uiMessenger().critical(tr("File Error"),
tr("Invalid MIDI data at %1").arg(inPath));
return;
}
}
else
{
bool isBig;
if (amuse::SongState::DetectVersion(data.data(), isBig) < 0)
{
g_MainWindow->uiMessenger().critical(tr("File Error"),
tr("Invalid song data at %1").arg(inPath));
return;
}
}
QFileInfo inInfo(inPath);
QString outPath =
QFileDialog::getSaveFileName(g_MainWindow, tr("Export SNG"),
QFileInfo(inInfo.path(), inInfo.completeBaseName() + QStringLiteral(".sng")).filePath(), tr("Song(*.sng)"));
if (outPath.isEmpty())
return;
QFile fo(outPath);
if (!fo.open(QFile::WriteOnly))
{
g_MainWindow->uiMessenger().critical(tr("File Error"),
tr("Unable to open %1 for writing: %1").arg(outPath).arg(fo.errorString()));
return;
}
fo.write((char*)data.data(), data.size());
}
void MIDIFileDelegate::pathChanged()
{
emit commitData(static_cast<MIDIFileFieldWidget*>(sender()));
@ -1210,7 +1370,8 @@ void MIDIPlayerWidget::stopped()
void MIDIPlayerWidget::resizeEvent(QResizeEvent* event)
{
m_button.setGeometry(event->size().width() - event->size().height(), 0, event->size().height(), event->size().height());
m_button.setGeometry(event->size().width() - event->size().height(), 0,
event->size().height(), event->size().height());
}
void MIDIPlayerWidget::mouseDoubleClickEvent(QMouseEvent* event)
@ -1219,6 +1380,17 @@ void MIDIPlayerWidget::mouseDoubleClickEvent(QMouseEvent* event)
event->ignore();
}
void MIDIPlayerWidget::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::RightButton)
{
QTableView* view = qobject_cast<QTableView*>(parentWidget()->parentWidget());
QAbstractItemDelegate* delegate = view->itemDelegateForColumn(1);
delegate->editorEvent(event, view->model(), {}, m_index);
}
event->ignore();
}
MIDIPlayerWidget::~MIDIPlayerWidget()
{
if (m_seq)
@ -1406,6 +1578,11 @@ void SongGroupEditor::setupDataChanged()
{
QString path = g_MainWindow->projectModel()->getMIDIPathOfSong(p.m_it->first);
QModelIndex index = m_setupList.index(idx, 1);
if (m_setupTable->m_listView->isPersistentEditorOpen(index))
{
++idx;
continue;
}
if (path.isEmpty())
{
m_setupTable->m_listView->setIndexWidget(index, nullptr);

View File

@ -4,7 +4,6 @@
#include "EditorWidget.hpp"
#include <QTabWidget>
#include <QAbstractTableModel>
#include <QStyledItemDelegate>
#include <QTableView>
#include <QToolButton>
#include <QAction>
@ -16,9 +15,11 @@
#include <QProxyStyle>
#include "amuse/Sequencer.hpp"
class PageObjectDelegate : public QStyledItemDelegate
class PageObjectDelegate : public BaseObjectDelegate
{
Q_OBJECT
protected:
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
public:
explicit PageObjectDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
@ -51,8 +52,14 @@ class MIDIFileDelegate : public QStyledItemDelegate
public:
explicit MIDIFileDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void destroyEditor(QWidget *editor, const QModelIndex &index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
bool editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option, const QModelIndex &index);
private slots:
void doExportMIDI();
void doExportSNG();
public slots:
void pathChanged();
};
@ -239,6 +246,8 @@ public:
void stopped();
void resizeEvent(QResizeEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
public slots:
void clicked();
};

View File

@ -120,7 +120,17 @@ public:
};
SFXObjectDelegate::SFXObjectDelegate(QObject* parent)
: QStyledItemDelegate(parent) {}
: BaseObjectDelegate(parent) {}
ProjectModel::INode* SFXObjectDelegate::getNode(const QAbstractItemModel* __model, const QModelIndex& index) const
{
const SFXModel* model = static_cast<const SFXModel*>(__model);
auto entry = model->m_sorted[index.row()];
if (entry->second.objId.id.id == 0xffff)
return nullptr;
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
return group->pageObjectNodeOfId(entry->second.objId.id);
}
QWidget* SFXObjectDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
@ -133,12 +143,8 @@ QWidget* SFXObjectDelegate::createEditor(QWidget* parent, const QStyleOptionView
void SFXObjectDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
const SFXModel* model = static_cast<const SFXModel*>(index.model());
auto entry = model->m_sorted[index.row()];
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
ProjectModel::BasePoolObjectNode* node = group->pageObjectNodeOfId(entry->second.objId.id);
int idx = 0;
if (node)
if (ProjectModel::BasePoolObjectNode* node = static_cast<ProjectModel::BasePoolObjectNode*>(getNode(index.model(), index)))
idx = g_MainWindow->projectModel()->getPageObjectProxy()->mapFromSource(g_MainWindow->projectModel()->index(node)).row();
static_cast<EditorFieldPageObjectNode*>(editor)->setCurrentIndex(idx);
if (static_cast<EditorFieldPageObjectNode*>(editor)->shouldPopupOpen())
@ -561,7 +567,8 @@ void SFXPlayerWidget::stopped()
void SFXPlayerWidget::resizeEvent(QResizeEvent* event)
{
m_button.setGeometry(event->size().width() - event->size().height(), 0, event->size().height(), event->size().height());
m_button.setGeometry(event->size().width() - event->size().height(), 0,
event->size().height(), event->size().height());
}
void SFXPlayerWidget::mouseDoubleClickEvent(QMouseEvent* event)
@ -570,6 +577,17 @@ void SFXPlayerWidget::mouseDoubleClickEvent(QMouseEvent* event)
event->ignore();
}
void SFXPlayerWidget::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::RightButton)
{
QTableView* view = qobject_cast<QTableView*>(parentWidget()->parentWidget());
QAbstractItemDelegate* delegate = view->itemDelegateForColumn(1);
delegate->editorEvent(event, view->model(), {}, m_index);
}
event->ignore();
}
SFXPlayerWidget::~SFXPlayerWidget()
{
if (m_vox)

View File

@ -6,9 +6,11 @@
#include <QTableView>
#include "amuse/Voice.hpp"
class SFXObjectDelegate : public QStyledItemDelegate
class SFXObjectDelegate : public BaseObjectDelegate
{
Q_OBJECT
protected:
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
public:
explicit SFXObjectDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
@ -98,6 +100,8 @@ public:
void stopped();
void resizeEvent(QResizeEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
public slots:
void clicked();
};

File diff suppressed because it is too large Load Diff

View File

@ -315,7 +315,7 @@ inline T ClampFull(float in)
{
if (std::is_floating_point<T>())
{
return std::min<T>(std::max<T>(in, -1.f), 1.f);
return in;
}
else
{

View File

@ -515,6 +515,7 @@ void Sequencer::ChannelState::killKeygroup(uint8_t kg, bool now)
m_lastVoice.reset();
if (now)
{
vox->kill();
it = m_chanVoxs.erase(it);
continue;
}
@ -533,6 +534,7 @@ void Sequencer::ChannelState::killKeygroup(uint8_t kg, bool now)
Voice* vox = it->get();
if (vox->m_keygroup == kg)
{
vox->kill();
it = m_keyoffVoxs.erase(it);
continue;
}

View File

@ -520,6 +520,8 @@ bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
uint16_t length = (m_parent->m_bigEndian ? SBig(*reinterpret_cast<const uint16_t*>(m_data + 2))
: *reinterpret_cast<const uint16_t*>(m_data + 2));
seq.keyOn(m_midiChan, note, vel);
if (length == 0)
seq.keyOff(m_midiChan, note, 0);
m_remNoteLengths[note] = length;
m_data += 4;
}
@ -559,6 +561,8 @@ bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
uint8_t note = m_data[2] & 0x7f;
uint8_t vel = m_data[3] & 0x7f;
seq.keyOn(m_midiChan, note, vel);
if (length == 0)
seq.keyOff(m_midiChan, note, 0);
m_remNoteLengths[note] = length;
}
else if (m_data[2] & 0x80 && m_data[3] & 0x80)