undo/redo for pattern list and sequence operations

portability/boost
zetaPRIME 2018-12-07 13:45:32 -05:00
parent c297ac40ba
commit 8238a15771
13 changed files with 356 additions and 92 deletions

13
notes
View File

@ -52,18 +52,17 @@ TODO {
- single-row pattern edits
? multi-select edits
- channel rename
- channel move
- channel rename
- channel add
- channel delete
pattern rename
pattern move
pattern add
pattern delete
(just have sequencer state stuff up there)
- pattern move
- pattern rename
- pattern add (w/ duplication and sequence insert, as single index)
- pattern delete (w/ resulting sequence edit)
generic sequencer edit
- generic sequencer edit
}
# fix how qt5.12 broke header text (removed elide for now)

View File

@ -34,3 +34,13 @@ pattern: {
} ] // if no channels have their last row filled, an integer representing the
// total number of rows is written as the last entry of the channel array
}
sample: {
"name": "asdf"
"type": "raw"
"rate": 48000
"channels": 2
"data" : [ len*channels float32s as raw bytefield ]
}
also sample variant using flac?

View File

@ -35,7 +35,7 @@ bool PatternDeltaCommand::mergeWith(const QUndoCommand* o_) {
auto* o = static_cast<const PatternDeltaCommand*>(o_);
if (o->pattern != pattern) return false;
if (o->ch != ch || o->rw != rw) return false;
row = static_cast<const PatternDeltaCommand*>(o)->row;
row = o->row;
return true;
}
@ -49,7 +49,32 @@ void PatternDeltaCommand::undo() {
emit pattern->project->socket->rowUpdated(pattern.get(), ch, rw);
}
PatternChannelMoveCommand::PatternChannelMoveCommand(const std::shared_ptr<Xybrid::Data::Pattern> &pattern, int from, int to) {
PatternRenameCommand::PatternRenameCommand(const std::shared_ptr<Pattern>& pattern, const std::string &to) {
this->pattern = pattern;
from = pattern->name;
this->to = to;
setText("rename pattern");
}
bool PatternRenameCommand::mergeWith(const QUndoCommand* o_) {
if (o_->id() != id()) return false;
auto* o = static_cast<const PatternRenameCommand*>(o_);
if (o->pattern != pattern) return false;
to = o->to;
return true;
}
void PatternRenameCommand::redo() {
pattern->name = to;
emit pattern->project->socket->updatePatternLists();
}
void PatternRenameCommand::undo() {
pattern->name = from;
emit pattern->project->socket->updatePatternLists();
}
PatternChannelMoveCommand::PatternChannelMoveCommand(const std::shared_ptr<Pattern> &pattern, int from, int to) {
this->pattern = pattern;
this->from = from;
this->to = to;
@ -79,7 +104,7 @@ void PatternChannelMoveCommand::undo() {
emit pattern->project->socket->patternUpdated(pattern.get());
}
PatternChannelRenameCommand::PatternChannelRenameCommand(const std::shared_ptr<Xybrid::Data::Pattern>& pattern, int channel, const std::string &to) {
PatternChannelRenameCommand::PatternChannelRenameCommand(const std::shared_ptr<Pattern>& pattern, int channel, const std::string &to) {
this->pattern = pattern;
idx = channel;
from = pattern->channel(idx).name;
@ -106,7 +131,7 @@ void PatternChannelRenameCommand::undo() {
emit pattern->project->socket->patternUpdated(pattern.get());
}
PatternChannelAddCommand::PatternChannelAddCommand(const std::shared_ptr<Xybrid::Data::Pattern>& pattern, int channel) {
PatternChannelAddCommand::PatternChannelAddCommand(const std::shared_ptr<Pattern>& pattern, int channel) {
this->pattern = pattern;
idx = channel;
setText("add channel");
@ -122,7 +147,7 @@ void PatternChannelAddCommand::undo() {
emit pattern->project->socket->patternUpdated(pattern.get());
}
PatternChannelDeleteCommand::PatternChannelDeleteCommand(const std::shared_ptr<Xybrid::Data::Pattern>& pattern, int channel) {
PatternChannelDeleteCommand::PatternChannelDeleteCommand(const std::shared_ptr<Pattern>& pattern, int channel) {
this->pattern = pattern;
idx = channel;
setText("delete channel");

View File

@ -33,6 +33,22 @@ namespace Xybrid::Editing {
void undo() override;
};
class PatternRenameCommand : public PatternCommand {
std::string from, to;
public:
PatternRenameCommand(const std::shared_ptr<Data::Pattern>& pattern, const std::string& to);
~PatternRenameCommand() override = default;
int id() const override {
return 2070;
}
bool mergeWith(const QUndoCommand*) override;
void redo() override;
void undo() override;
};
class PatternChannelMoveCommand : public PatternCommand {
int from, to;

View File

@ -1,8 +0,0 @@
#pragma once
namespace Xybrid::Editing {
class PatternLens {
public:
//
};
}

View File

@ -0,0 +1,141 @@
#include "projectcommands.h"
#include "uisocket.h"
#include "mainwindow.h"
#include <QDebug>
#include <QUndoStack>
using Xybrid::Data::Project;
using Xybrid::Data::Pattern;
using namespace Xybrid::Editing;
bool ProjectCommand::commit() {
if (!project->socket || !project->socket->undoStack) return false;
project->socket->undoStack->push(this);
return true;
}
bool ProjectCommand::cancel() {
delete this;
return false;
}
ProjectSequencerDeltaCommand::ProjectSequencerDeltaCommand(const std::shared_ptr<Project>& project) {
this->project = project;
oldSeq = project->sequence;
oldSeqSel = project->socket->window->sequenceSelection();
seq = oldSeq;
seqSel = oldSeqSel;
setText("edit sequence");
}
bool ProjectSequencerDeltaCommand::mergeWith(const QUndoCommand* o_) {
if (o_->id() != id()) return false;
auto* o = static_cast<const ProjectSequencerDeltaCommand*>(o_);
if (o->project != project) return false;
seq = o->seq;
seqSel = o->seqSel;
return true;
}
void ProjectSequencerDeltaCommand::redo() {
project->sequence = seq;
emit project->socket->updatePatternLists();
project->socket->window->sequenceSelection(seqSel);
}
void ProjectSequencerDeltaCommand::undo() {
project->sequence = oldSeq;
emit project->socket->updatePatternLists();
project->socket->window->sequenceSelection(oldSeqSel);
}
ProjectPatternMoveCommand::ProjectPatternMoveCommand(const std::shared_ptr<Project>& project, int f, int t) {
this->project = project;
from = f;
to = t;
setText("move pattern");
}
bool ProjectPatternMoveCommand::mergeWith(const QUndoCommand* o_) {
if (o_->id() != id()) return false;
auto* o = static_cast<const ProjectPatternMoveCommand*>(o_);
if (o->project != project) return false;
to = o->to;
return true;
}
void ProjectPatternMoveCommand::redo() {
auto p = project->patterns[static_cast<size_t>(from)];
project->patterns.erase(project->patterns.begin() + from);
project->patterns.insert(project->patterns.begin() + to, p);
project->updatePatternIndices();
emit project->socket->updatePatternLists();
}
void ProjectPatternMoveCommand::undo() {
auto p = project->patterns[static_cast<size_t>(to)];
project->patterns.erase(project->patterns.begin() + to);
project->patterns.insert(project->patterns.begin() + from, p);
project->updatePatternIndices();
emit project->socket->updatePatternLists();
}
ProjectPatternAddCommand::ProjectPatternAddCommand(const std::shared_ptr<Project>& project, int at, int atSeq, const std::shared_ptr<Pattern>& copyOf) {
this->project = project;
if (at < 0) at = std::numeric_limits<int>::max();
this->at = std::max(0, std::min(at, static_cast<int>(project->patterns.size())));
this->atSeq = atSeq;
this->copyOf = copyOf;
if (copyOf) setText("duplicate pattern");
else setText("add pattern");
}
void ProjectPatternAddCommand::redo() {
auto np = project->newPattern(static_cast<size_t>(at));
if (copyOf) *np = *copyOf;
project->updatePatternIndices();
if (atSeq > -1) {
project->sequence.insert(project->sequence.begin() + atSeq, np.get());
emit project->socket->updatePatternLists();
project->socket->window->patternSelection(at);
project->socket->window->sequenceSelection(atSeq);
} else {
emit project->socket->updatePatternLists();
project->socket->window->patternSelection(at);
}
}
void ProjectPatternAddCommand::undo() {
project->removePattern(project->patterns[static_cast<size_t>(at)].get()); // ???
emit project->socket->updatePatternLists();
if (atSeq > -1) project->socket->window->sequenceSelection(std::max(0, atSeq - 1));
else if (copyOf) project->socket->window->patternSelection(static_cast<int>(copyOf->index));
}
ProjectPatternDeleteCommand::ProjectPatternDeleteCommand(const std::shared_ptr<Project>& project, const std::shared_ptr<Pattern>& pattern) {
this->project = project;
this->pattern = pattern;
setText("delete pattern");
oldSeq = project->sequence;
oldSeqSel = project->socket->window->sequenceSelection();
}
void ProjectPatternDeleteCommand::redo() {
int seqSel = oldSeqSel;
for (int i = 0; i <= oldSeqSel; i++) if (seqSel > 0 && project->sequence[static_cast<size_t>(i)] == pattern.get()) seqSel--;
project->sequence.erase(std::remove(project->sequence.begin(), project->sequence.end(), pattern.get()), project->sequence.end());
project->patterns.erase(project->patterns.begin() + static_cast<ptrdiff_t>(pattern->index));
project->updatePatternIndices();
emit project->socket->updatePatternLists();
project->socket->window->sequenceSelection(seqSel);
}
void ProjectPatternDeleteCommand::undo() {
project->patterns.insert(project->patterns.begin() + static_cast<ptrdiff_t>(pattern->index), pattern);
project->updatePatternIndices();
project->sequence = oldSeq;
emit project->socket->updatePatternLists();
project->socket->window->sequenceSelection(oldSeqSel);
}

View File

@ -0,0 +1,75 @@
#pragma once
#include "data/project.h"
#include <QUndoCommand>
namespace Xybrid::Editing {
class ProjectCommand : public QUndoCommand {
//
protected:
std::shared_ptr<Data::Project> project;
public:
bool commit();
bool cancel();
};
class ProjectSequencerDeltaCommand : public ProjectCommand {
std::vector<Data::Pattern*> oldSeq;
int oldSeqSel;
public:
std::vector<Data::Pattern*> seq;
int seqSel;
ProjectSequencerDeltaCommand(const std::shared_ptr<Data::Project>& project);
~ProjectSequencerDeltaCommand() override = default;
int id() const override {
return 1000;
}
bool mergeWith(const QUndoCommand*) override;
void redo() override;
void undo() override;
};
class ProjectPatternMoveCommand : public ProjectCommand {
int from, to;
public:
ProjectPatternMoveCommand(const std::shared_ptr<Data::Project>& project, int from, int to);
~ProjectPatternMoveCommand() override = default;
int id() const override {
return 1001;
}
bool mergeWith(const QUndoCommand*) override;
void redo() override;
void undo() override;
};
class ProjectPatternAddCommand : public ProjectCommand {
int at, atSeq;
std::shared_ptr<Data::Pattern> copyOf;
public:
ProjectPatternAddCommand(const std::shared_ptr<Data::Project>& project, int at, int atSeq = -1, const std::shared_ptr<Data::Pattern>& copyOf = nullptr);
~ProjectPatternAddCommand() override = default;
void redo() override;
void undo() override;
};
class ProjectPatternDeleteCommand : public ProjectCommand {
std::shared_ptr<Data::Pattern> pattern;
std::vector<Data::Pattern*> oldSeq;
int oldSeqSel;
public:
ProjectPatternDeleteCommand(const std::shared_ptr<Data::Project>& project, const std::shared_ptr<Data::Pattern>& pattern);
~ProjectPatternDeleteCommand() override = default;
void redo() override;
void undo() override;
};
}

View File

@ -18,6 +18,8 @@ using Xybrid::MainWindow;
#include "ui/patternsequencermodel.h"
#include "ui/patterneditoritemdelegate.h"
#include "editing/projectcommands.h"
using Xybrid::Data::Project;
using Xybrid::Data::Pattern;
@ -26,6 +28,8 @@ using Xybrid::UI::PatternSequencerModel;
using Xybrid::UI::PatternEditorModel;
using Xybrid::UI::PatternEditorItemDelegate;
using namespace Xybrid::Editing;
namespace {
constexpr const auto projectFilter = u8"Xybrid project (*.xyp)\nAll files (*)";
}
@ -87,23 +91,16 @@ MainWindow::MainWindow(QWidget *parent) :
QMenu* menu = new QMenu(this);
menu->addAction("New Pattern", [this, idx]() {
auto np = project->newPattern(idx);
updatePatternLists();
selectPatternForEditing(np.get());
(new ProjectPatternAddCommand(project, static_cast<int>(idx)))->commit();
});
if (p) {
menu->addAction("Duplicate Pattern", [this, p, idx]() {
auto np = project->newPattern(idx + 1);
*np = *p;
project->updatePatternIndices();
updatePatternLists();
selectPatternForEditing(np.get());
(new ProjectPatternAddCommand(project, static_cast<int>(idx) + 1, -1, p))->commit();
});
menu->addSeparator();
menu->addAction("Delete Pattern", [this, p]() {
if (QMessageBox::warning(this, "Are you sure?", QString("Remove pattern %1?").arg(Util::numAndName(p->index, p->name)), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) return;
project->removePattern(p.get());
updatePatternLists();
(new ProjectPatternDeleteCommand(project, p))->commit();
});
}
menu->popup(ui->patternList->mapToGlobal(pt));
@ -130,49 +127,38 @@ MainWindow::MainWindow(QWidget *parent) :
// rightclick menu
connect(ui->patternSequencer, &QTableView::customContextMenuRequested, [this](const QPoint& pt) {
size_t idx = static_cast<size_t>(ui->patternSequencer->indexAt(pt).column());
/*std::shared_ptr<Pattern> p = nullptr;
if (idx < project->sequence.size()) {
Pattern* pr = project->sequence[idx];
}*/
QMenu* menu = new QMenu(this);
menu->addAction("Insert Pattern", [this, idx]() {
if (!editingPattern->validFor(project)) return; // nope
int si = static_cast<int>(std::min(idx, project->sequence.size()));
project->sequence.insert(project->sequence.begin() + si, editingPattern.get());
updatePatternLists();
ui->patternSequencer->setCurrentIndex(ui->patternSequencer->model()->index(0, si+1));
auto* c = new ProjectSequencerDeltaCommand(project);
c->seq.insert(c->seq.begin() + si, editingPattern.get());
c->seqSel = si+1;
c->commit();
});
menu->addAction("Insert Separator", [this, idx]() {
int si = static_cast<int>(std::min(idx, project->sequence.size()));
project->sequence.insert(project->sequence.begin() + si, nullptr);
updatePatternLists();
ui->patternSequencer->setCurrentIndex(ui->patternSequencer->model()->index(0, si+1));
auto* c = new ProjectSequencerDeltaCommand(project);
c->seq.insert(c->seq.begin() + si, nullptr);
c->seqSel = si+1;
c->commit();
});
if (idx < project->sequence.size()) menu->addAction("Remove", [this, idx]() {
project->sequence.erase(project->sequence.begin() + static_cast<ptrdiff_t>(idx));
updatePatternLists();
ui->patternSequencer->setCurrentIndex(ui->patternSequencer->model()->index(0, static_cast<int>(idx)-1));
auto* c = new ProjectSequencerDeltaCommand(project);
c->seq.erase(c->seq.begin() + static_cast<ptrdiff_t>(idx));
c->seqSel = static_cast<int>(idx)-1;
c->commit();
});
menu->addSeparator();
menu->addAction("Create New Pattern", [this, idx]() {
auto np = project->newPattern();
int si = static_cast<int>(std::min(idx, project->sequence.size()));
project->sequence.insert(project->sequence.begin() + si, np.get());
updatePatternLists();
selectPatternForEditing(np.get());
ui->patternSequencer->setCurrentIndex(ui->patternSequencer->model()->index(0, si));
(new ProjectPatternAddCommand(project, -1, si))->commit();
});
if (idx < project->sequence.size() && project->sequence[idx]) {
menu->addAction("Duplicate Pattern", [this, idx, p = project->patterns[project->sequence[idx]->index]]() {
auto np = project->newPattern(p->index + 1);
*np = *p;
project->updatePatternIndices();
int si = static_cast<int>(std::min(idx + 1, project->sequence.size()));
project->sequence.insert(project->sequence.begin() + si, np.get());
updatePatternLists();
selectPatternForEditing(np.get());
ui->patternSequencer->setCurrentIndex(ui->patternSequencer->model()->index(0, si));
(new ProjectPatternAddCommand(project, static_cast<int>(p->index) + 1, si, p))->commit();
});
}
@ -204,6 +190,7 @@ MainWindow::MainWindow(QWidget *parent) :
// Set up signaling from project to UI
socket.reset(new UISocket());
socket->window = this;
socket->undoStack = undoStack;
connect(socket.get(), &UISocket::updatePatternLists, this, &MainWindow::updatePatternLists);
connect(socket.get(), &UISocket::patternUpdated, [this](Pattern* p) {
@ -295,9 +282,23 @@ void MainWindow::onNewProjectLoaded() {
updateTitle();
}
int MainWindow::patternSelection(int n) {
auto i = ui->patternList->currentIndex();
if (n >= -1) ui->patternList->setCurrentIndex(ui->patternList->model()->index(n, 0));
return i.isValid() ? i.row() : -1;
}
int MainWindow::sequenceSelection(int n) {
auto i = ui->patternSequencer->currentIndex();
if (n >= -1) ui->patternSequencer->setCurrentIndex(ui->patternSequencer->model()->index(0, n));
return i.isValid() ? i.column() : -1;
}
void MainWindow::updatePatternLists() {
emit ui->patternList->model()->layoutChanged();
emit ui->patternSequencer->model()->layoutChanged();
if (auto i = ui->patternList->currentIndex(); i.isValid() && !ui->patternSequencer->currentIndex().isValid())
selectPatternForEditing(project->patterns[static_cast<size_t>(i.row())].get()); // make sure pattern editor matches selection
if (editingPattern && !editingPattern->validFor(project)) // if current pattern invalidated, select new one
selectPatternForEditing(project->patterns[std::min(editingPattern->index, project->patterns.size() - 1)].get());
}

View File

@ -35,10 +35,13 @@ namespace Xybrid {
void updateTitle();
public:
Data::Project* getProject() const {
return project.get();
const std::shared_ptr<Data::Project>& getProject() const {
return project;
}
int patternSelection(int = -100);
int sequenceSelection(int = -100);
protected:
bool eventFilter(QObject *obj, QEvent *event) override;

View File

@ -4,6 +4,10 @@ using Xybrid::UI::PatternListModel;
using Xybrid::Data::Project;
using Xybrid::Data::Pattern;
#include "editing/projectcommands.h"
#include "editing/patterncommands.h"
using namespace Xybrid::Editing;
#include <QDebug>
#include <QMimeData>
@ -14,7 +18,7 @@ PatternListModel::PatternListModel(QObject *parent, MainWindow* window) : QAbstr
}
int PatternListModel::rowCount(const QModelIndex &parent [[maybe_unused]]) const {
auto* project = window->getProject();
auto* project = window->getProject().get();
if (!project) return 0;
return static_cast<int>(project->patterns.size());
}
@ -24,7 +28,7 @@ int PatternListModel::columnCount(const QModelIndex &parent [[maybe_unused]]) co
}
QVariant PatternListModel::data(const QModelIndex &index, int role) const {
auto* project = window->getProject();
auto* project = window->getProject().get();
if (!project) return QVariant();
if (role == Qt::DisplayRole) {
auto pattern = project->patterns[static_cast<size_t>(index.row())];
@ -38,10 +42,11 @@ QVariant PatternListModel::data(const QModelIndex &index, int role) const {
bool PatternListModel::setData(const QModelIndex &index, const QVariant &value, int role) {
if (role == Qt::EditRole) {
auto* project = window->getProject();
auto* project = window->getProject().get();
if (!project) return true;
auto pattern = project->patterns[static_cast<size_t>(index.row())];
pattern->name = value.toString().toStdString();
//pattern->name = value.toString().toStdString();
return (new PatternRenameCommand(pattern, value.toString().toStdString()))->commit();
}
return true;
}
@ -65,7 +70,7 @@ QMimeData *PatternListModel::mimeData(const QModelIndexList &indexes) const {
QByteArray dd;
QDataStream stream(&dd, QIODevice::WriteOnly);
size_t idx = static_cast<size_t>(indexes[0].row());
Project* prj = window->getProject();
Project* prj = window->getProject().get();
if (!prj) return d; // if somehow nullptr, just return a blank
stream.writeRawData(reinterpret_cast<char*>(&idx), sizeof(size_t));
stream.writeRawData(reinterpret_cast<char*>(&prj), sizeof(void*));
@ -85,7 +90,7 @@ bool PatternListModel::dropMimeData(const QMimeData *data, Qt::DropAction action
Project* prj;
stream.readRawData(reinterpret_cast<char*>(&idx), sizeof(size_t));
stream.readRawData(reinterpret_cast<char*>(&prj), sizeof(void*));
if (prj != window->getProject()) return false; // wrong or invalid project
if (prj != window->getProject().get()) return false; // wrong or invalid project
if (idx >= prj->patterns.size()) return false; // index out of range
p = prj->patterns[idx];
}
@ -97,11 +102,5 @@ bool PatternListModel::dropMimeData(const QMimeData *data, Qt::DropAction action
if (row < 0) row = static_cast<int>(p->project->patterns.size()); // if dropped on empty space, snap to end
if (row > static_cast<int>(p->index)) row -= 1; // compensate ahead of time for snap-out
p->project->patterns.erase(p->project->patterns.begin() + static_cast<int>(p->index));
p->project->patterns.insert(p->project->patterns.begin() + row, p);
p->project->updatePatternIndices();
emit p->project->socket->updatePatternLists();
return true;
return (new ProjectPatternMoveCommand(window->getProject(), static_cast<int>(p->index), row))->commit();
}

View File

@ -8,6 +8,9 @@ using Xybrid::Data::Project;
#include "mainwindow.h"
#include "editing/projectcommands.h"
using namespace Xybrid::Editing;
PatternSequencerModel::PatternSequencerModel(QObject *parent, MainWindow* window) : QAbstractTableModel (parent) {
this->window = window;
}
@ -17,13 +20,13 @@ int PatternSequencerModel::rowCount(const QModelIndex &parent [[maybe_unused]])
}
int PatternSequencerModel::columnCount(const QModelIndex &parent [[maybe_unused]]) const {
auto* project = window->getProject();
auto* project = window->getProject().get();
if (!project) return 0;
return static_cast<int>(window->getProject()->sequence.size() + 1);
}
QVariant PatternSequencerModel::data(const QModelIndex &index, int role) const {
auto* project = window->getProject();
auto* project = window->getProject().get();
if (!project) return QVariant();
if (role == Qt::DisplayRole || role == Qt::ToolTipRole) {
bool toolTip = (role == Qt::ToolTipRole);
@ -59,7 +62,7 @@ QMimeData* PatternSequencerModel::mimeData(const QModelIndexList &indexes) const
QByteArray dd;
QDataStream stream(&dd, QIODevice::WriteOnly);
size_t idx = static_cast<size_t>(indexes[0].column());
Project* prj = window->getProject();
Project* prj = window->getProject().get();
if (!prj) return d; // if somehow nullptr, just return a blank
stream.writeRawData(reinterpret_cast<char*>(&idx), sizeof(size_t));
stream.writeRawData(reinterpret_cast<char*>(&prj), sizeof(void*));
@ -79,21 +82,19 @@ bool PatternSequencerModel::dropMimeData(const QMimeData *data, Qt::DropAction a
Project* prj;
stream.readRawData(reinterpret_cast<char*>(&idx), sizeof(size_t));
stream.readRawData(reinterpret_cast<char*>(&prj), sizeof(void*));
if (prj != window->getProject()) return false; // wrong or invalid project
if (prj != window->getProject().get()) return false; // wrong or invalid project
if (idx >= prj->patterns.size()) return false; // index out of range
p = prj->patterns[idx].get();
}
if (parent.isValid()) { // if dropped onto an item and not between, place on opposite side
column = parent.column();
if (column > static_cast<int>(p->index)) column += 1;
}
if (parent.isValid()) column = parent.column(); // always insert before
if (column < 0 || column > static_cast<int>(p->project->sequence.size())) column = static_cast<int>(p->project->sequence.size()); // if dropped on empty space, snap to end
p->project->sequence.insert(p->project->sequence.begin() + column, p);
auto* c = new ProjectSequencerDeltaCommand(window->getProject());
c->seq.insert(c->seq.begin() + column, p);
c->seqSel = column;
emit layoutChanged();
return true;
return c->commit();
}
if (data->hasFormat("xybrid-internal/x-sequence-index")) {
if (action == Qt::IgnoreAction) return true; // can accept type
@ -106,7 +107,7 @@ bool PatternSequencerModel::dropMimeData(const QMimeData *data, Qt::DropAction a
QDataStream stream(&dd, QIODevice::ReadOnly);
stream.readRawData(reinterpret_cast<char*>(&idx), sizeof(size_t));
stream.readRawData(reinterpret_cast<char*>(&prj), sizeof(void*));
if (prj != window->getProject()) return false; // wrong or invalid project
if (prj != window->getProject().get()) return false; // wrong or invalid project
if (idx >= prj->sequence.size()) return false; // index out of range
}
@ -118,14 +119,13 @@ bool PatternSequencerModel::dropMimeData(const QMimeData *data, Qt::DropAction a
if (!copy && column > static_cast<int>(idx)) column -= 1; // compensate ahead of time for snap-out
Pattern* p = prj->sequence[idx];
if (!copy) prj->sequence.erase(prj->sequence.begin() + static_cast<int>(idx));
prj->sequence.insert(prj->sequence.begin() + column, p);
auto* c = new ProjectSequencerDeltaCommand(window->getProject());
Pattern* p = c->seq[idx];
if (!copy) c->seq.erase(c->seq.begin() + static_cast<int>(idx));
c->seq.insert(c->seq.begin() + column, p);
c->seqSel = column;
prj->updatePatternIndices();
emit layoutChanged();
return true;
return c->commit();
}
return false;

View File

@ -10,9 +10,11 @@ namespace Xybrid::Data {
}
namespace Xybrid {
class MainWindow;
class UISocket : public QObject {
Q_OBJECT
public:
MainWindow* window;
QUndoStack* undoStack;
signals:

View File

@ -39,7 +39,8 @@ SOURCES += \
ui/patternlistmodel.cpp \
config/colorscheme.cpp \
fileops.cpp \
editing/patterncommands.cpp
editing/patterncommands.cpp \
editing/projectcommands.cpp
HEADERS += \
mainwindow.h \
@ -55,8 +56,8 @@ HEADERS += \
util/strings.h \
config/colorscheme.h \
fileops.h \
editing/patternlens.h \
editing/patterncommands.h
editing/patterncommands.h \
editing/projectcommands.h
FORMS += \
mainwindow.ui