undo/redo for pattern list and sequence operations
parent
c297ac40ba
commit
8238a15771
13
notes
13
notes
|
@ -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)
|
||||
|
|
10
save format
10
save format
|
@ -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?
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace Xybrid::Editing {
|
||||
class PatternLens {
|
||||
public:
|
||||
//
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -10,9 +10,11 @@ namespace Xybrid::Data {
|
|||
}
|
||||
|
||||
namespace Xybrid {
|
||||
class MainWindow;
|
||||
class UISocket : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MainWindow* window;
|
||||
QUndoStack* undoStack;
|
||||
|
||||
signals:
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue