xybrid/xybrid/ui/patterneditorview.cpp

196 lines
7.2 KiB
C++

#include "patterneditorview.h"
using Xybrid::UI::PatternEditorView;
#include "ui/patterneditormodel.h"
using Xybrid::UI::PatternEditorModel;
#include "ui/patterneditoritemdelegate.h"
using Xybrid::UI::PatternEditorItemDelegate;
#include "util/strings.h"
#include "ui/channelheaderview.h"
using Xybrid::UI::ChannelHeaderView;
#include "data/project.h"
using Xybrid::Data::Project;
using Xybrid::Data::Pattern;
#include "editing/patterncommands.h"
using namespace Xybrid::Editing;
#include <QKeyEvent>
#include <QDebug>
#include <QHeaderView>
#include <QScrollBar>
#include <QHBoxLayout>
#include <QMenu>
#include <QTextEdit>
#include <QInputDialog>
#include <QMessageBox>
namespace {
std::shared_ptr<Pattern> pt = std::make_shared<Pattern>(1, 0); // fallback pattern
}
PatternEditorView::PatternEditorView(QWidget *parent) : QTableView(parent) {
hdr.reset(new ChannelHeaderView(this));
hdr->setSectionResizeMode(QHeaderView::Fixed);
hdr->setTextElideMode(Qt::ElideNone);//Middle);
hdr->setStretchLastSection(true);
hdr->setSectionsMovable(true);
hdr->setFirstSectionMovable(true);
hdr->setEditTriggers(EditTrigger::DoubleClicked);
// hook up scrolling to update header position
connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, &PatternEditorView::updateHeaderOffset);
// and header swap
connect(hdr.get(), &QHeaderView::sectionMoved, this, &PatternEditorView::headerMoved);
// hook up context menu
hdr->setContextMenuPolicy(Qt::CustomContextMenu);
connect(hdr.get(), &QHeaderView::customContextMenuRequested, this, &PatternEditorView::headerContextMenu);
connect(hdr.get(), &QHeaderView::sectionDoubleClicked, this, &PatternEditorView::headerDoubleClicked);
horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);//ResizeToContents);
horizontalHeader()->setStretchLastSection(true);
verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
verticalHeader()->setStretchLastSection(true);
verticalHeader()->setSectionsClickable(false);
setCornerButtonEnabled(false);
//verticalHeader()->setDefaultAlignment(Qt::AlignTop);
cornerBoxBox.reset(new QWidget(this));
QHBoxLayout *layoutCheckBox = new QHBoxLayout(cornerBoxBox.get());
cornerBox.reset(new QCheckBox());
layoutCheckBox->addWidget(cornerBox.get());
layoutCheckBox->setAlignment(Qt::AlignCenter);
layoutCheckBox->setContentsMargins(0,0,0,0);
cornerBox->setToolTip("Expand to fit names");
cornerBox->setFocusPolicy(Qt::NoFocus);
connect(cornerBox.get(), &QCheckBox::toggled, this, [this](bool state) {
mdl->fitHeaderToName = state;
mdl->updateColumnDisplay();
});
mdl.reset(new PatternEditorModel(this));
del.reset(new PatternEditorItemDelegate(this));
setItemDelegate(&*del);
mdl->setPattern(pt);
setModel(&*mdl);
hdr->setModel(&*mdl->hprox);
}
PatternEditorView::~PatternEditorView() {
//
/*mdl.release();
del.release();
hdr.release();
cornerBoxBox.release();
cornerBox.release();*/
//horizontalHeader()->deleteLater();
}
void PatternEditorView::keyPressEvent(QKeyEvent *event) {
if (/*event->modifiers() & Qt::Modifier::CTRL &&*/ (event->key() == Qt::Key_Tab || event->key() == Qt::Key_Backtab)) { // don't block ctrl+tab
event->ignore();
return;
//QKeyEvent()
}
if (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Insert) {
if (!edit(currentIndex(), AnyKeyPressed, event)) {
event->ignore();
return;
}
}
QAbstractItemView::keyPressEvent(event);
}
void PatternEditorView::setPattern(const std::shared_ptr<Pattern>& pattern) {
mdl->setPattern(pattern);
}
void PatternEditorView::updateGeometries() {
if (mdl && colUpdateNeeded && horizontalHeader()->count() > 0) {
mdl->updateColumnDisplay();
colUpdateNeeded = false;
} else updateHeader(true); // do this once on every geom update
if (cornerBox) {
cornerBoxBox->setGeometry(verticalHeader()->x(), horizontalHeader()->y(), verticalHeader()->width(), horizontalHeader()->height());
//cornerBox->move(verticalHeader()->x() + verticalHeader()->width() / 2 - cornerBox->minimumWidth() / 2, horizontalHeader()->y() + cornerBox->height() / 2);
}
this->QTableView::updateGeometries();
}
void PatternEditorView::updateHeader(bool full) {
auto* bh = horizontalHeader(); // base header
if (full) {
hdr->reset(); // force section update
for (int i = 0; i < hdr->count(); i++) { // set sizes
constexpr int cpc = PatternEditorModel::colsPerChannel;
int w = 0;
for (int j = 0; j < cpc; j++) {
w += bh->sectionSize(i*cpc+j);
}
hdr->resizeSection(i, w);
}
}
hdr->setGeometry(bh->x(), bh->y(), bh->width(), bh->height());
hdr->setOffset(bh->offset());
}
void PatternEditorView::refresh() {
mdl->refresh();
}
void PatternEditorView::updateHeaderOffset(int) {
updateHeader(false);
}
void PatternEditorView::headerMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) {
if (logicalIndex == newVisualIndex) return; // assume moving back
hdr->moveSection(newVisualIndex, logicalIndex); // maintain straight-through order
if (logicalIndex >= hdr->count() - 1) return; // no dragging the endcap :|
(new PatternChannelMoveCommand(mdl->getPattern(), oldVisualIndex, newVisualIndex))->commit();
}
void PatternEditorView::headerDoubleClicked(int section) {
startRenameChannel(section);
}
void PatternEditorView::headerContextMenu(QPoint pt) {
int idx = hdr->logicalIndexAt(pt);
std::shared_ptr<Pattern> p = mdl->getPattern();
QMenu* menu = new QMenu(this);
menu->addAction("Add Channel", this, [/*this,*/ idx, p]() {
(new PatternChannelAddCommand(p, idx))->commit();
});
if (idx < hdr->count() - 1) {
menu->addAction("Delete Channel", this, [this, idx, p]() {
if (QMessageBox::warning(this, "Are you sure?", QString("Remove channel %1 from pattern %2?").arg(Util::numAndName(idx, p->channel(idx).name)).arg(Util::numAndName(p->index, p->name)), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) return;
(new PatternChannelDeleteCommand(p, idx))->commit();
});
menu->addAction("Rename Channel...", this, [this, idx, p]() {
if (p != mdl->getPattern()) return; // swapped already
startRenameChannel(idx);
});
}
menu->setAttribute(Qt::WA_DeleteOnClose);
menu->popup(hdr->mapToGlobal(pt));
}
void PatternEditorView::startRenameChannel(int channel) {
auto p = mdl->getPattern();
if (static_cast<size_t>(channel) >= p->numChannels()) return;
auto c = &p->channel(channel);
bool ok = false;
auto capt = QString("Rename channel %1:").arg(channel);
auto n = QInputDialog::getText(this, "Rename...", capt, QLineEdit::Normal, QString::fromStdString(c->name), &ok);
if (!ok) return; // canceled
if (p != mdl->getPattern() || c != &p->channel(channel)) return; // abort if this somehow isn't the channel it was before
(new PatternChannelRenameCommand(p, channel, n.toStdString()))->commit();
}