#include "groupview.hpp" #include "library.hpp" #include "painter.hpp" #include "src/utils.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include group_view::group_view() : QMdiSubWindow() { auto sp = new QSplitter(this); this->setWidget(sp); auto leftpane = new QWidget(); auto l1 = new QVBoxLayout(); leftpane->setLayout(l1); tv = new QTableView(); m = new QStandardItemModel(this); mf = new QSortFilterProxyModel(this); mf->setSourceModel(m); mf->setFilterCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive); tv->setModel(mf); tv->setColumnHidden(3, true); tv->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection); tv->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows); connect(tv->selectionModel(), &QItemSelectionModel::currentRowChanged, [this](const QModelIndex &curidx, const QModelIndex &oldidx) { if (oldidx.isValid() && curidx.row() == oldidx.row()) return; bool dirty = oldidx.isValid() && this->dirty; if (oldidx.isValid()) { int64_t oldgid = mf->data(oldidx.siblingAtColumn(3), Qt::ItemDataRole::DisplayRole).toLongLong(); auto oldgroup = l->get_group(oldgid); dirty |= tetitle->text() != QString::fromStdString(oldgroup.title); dirty |= teauthor->text() != QString::fromStdString(oldgroup.author); dirty |= sbh->value() != oldgroup.hc; dirty |= sbv->value() != oldgroup.vc; } if (dirty) { if (QMessageBox::question(this, "Unsaved changes", "Changes to map art not saved! Discard and switch to another map art?") != QMessageBox::StandardButton::Yes) { int row = oldidx.row(); QMetaObject::invokeMethod(this, [this, row]() { QSignalBlocker b(tv->selectionModel()); tv->selectRow(row); }, Qt::ConnectionType::QueuedConnection); return; } } this->update_fields(); }); l1->addWidget(tv); auto l2 = new QHBoxLayout(); pbadd = new QPushButton("+"); pbrem = new QPushButton("-"); tefilter = new QLineEdit(); tefilter->setPlaceholderText("Filter"); connect(pbadd, &QPushButton::pressed, this, &group_view::add_group); connect(pbrem, &QPushButton::pressed, this, &group_view::rem_group); l2->addWidget(tefilter); l2->addWidget(pbadd); l2->addWidget(pbrem); l1->addLayout(l2); sp->addWidget(leftpane); auto rightpane = new QWidget(); auto l3 = new QVBoxLayout(); rightpane->setLayout(l3); p = new map_painter(this); l3->addWidget(p->view()); connect(p, &map_painter::map_id_changed, this, &group_view::painter_drop); tetitle = new QLineEdit(); teauthor = new QLineEdit(); tetitle->setPlaceholderText("Title"); teauthor->setPlaceholderText("Author(s)"); l3->addWidget(tetitle); l3->addWidget(teauthor); auto l4 = new QHBoxLayout(); sbv = new QSpinBox(); sbh = new QSpinBox(); sbv->setValue(1); sbv->setMinimum(1); sbv->setMaximum(100); connect(sbv, QOverload::of(&QSpinBox::valueChanged), this, &group_view::reset_dim); sbh->setValue(1); sbh->setMinimum(1); sbh->setMaximum(100); connect(sbh, QOverload::of(&QSpinBox::valueChanged), this, &group_view::reset_dim); l4->addWidget(sbh); l4->addWidget(new QLabel("x")); l4->addWidget(sbv); l4->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); l4->addWidget(new QLabel("Zoom")); cbscale = new QComboBox(); cbscale->addItems({QStringLiteral("100%"), QStringLiteral("200%"), QStringLiteral("300%"), QStringLiteral("400%")}); connect(cbscale, &QComboBox::currentIndexChanged, this, [this](int idx) { p->set_scaling(idx + 1); }); l4->addWidget(cbscale); pbapply = new QPushButton("Save"); connect(pbapply, &QPushButton::pressed, this, &group_view::update_library); l4->addWidget(pbapply); l3->addLayout(l4); sp->addWidget(rightpane); sp->setStretchFactor(0, 1); sp->setStretchFactor(1, 3); sp->setCollapsible(0, false); sp->setCollapsible(1, false); l = nullptr; dirty = false; connect(tefilter, &QLineEdit::textChanged, mf, &QSortFilterProxyModel::setFilterFixedString); this->setWindowTitle("Map art listings"); this->setAttribute(Qt::WA_DeleteOnClose, false); } group_view::~group_view() { delete p; } void group_view::set_library(map_library *lib) { l = lib; p->set_map_library(l); refresh_list(); } void group_view::export_group_image(QString fn, int64_t gid) { if (!l || !l->has_group(gid)) return; QImage img = image_of_map_group(l, l->get_group(gid), 1.); img.save(fn, "PNG"); } void group_view::add_group() { map_group_t g { std::string(), std::string(), 1, 1, {0}, {false} }; l->new_group(g); refresh_list(); tv->scrollTo(mf->index(mf->rowCount() - 1, 0)); } void group_view::rem_group() { if (!tv->currentIndex().isValid()) return; int64_t curgid = mf->data(tv->currentIndex().siblingAtColumn(3), Qt::ItemDataRole::DisplayRole).toLongLong(); l->remove_group(curgid); refresh_list(); } void group_view::update_fields() { if (!tv->currentIndex().isValid()) return; int64_t curgid = mf->data(tv->currentIndex().siblingAtColumn(3), Qt::ItemDataRole::DisplayRole).toLongLong(); current_group = l->get_group(curgid); auto &g = current_group; tetitle->setText(QString::fromStdString(g.title)); teauthor->setText(QString::fromStdString(g.author)); QSignalBlocker bh(sbh); QSignalBlocker bv(sbv); sbh->setValue(g.hc); sbv->setValue(g.vc); update_map_view(); dirty = false; } void group_view::painter_drop(int pos, bool populated, int id) { if (!tv->currentIndex().isValid()) return; auto &g = current_group; g.populated[pos] = populated; g.ids[pos] = id; dirty = true; } void group_view::update_library() { if (!tv->currentIndex().isValid()) return; int64_t curgid = mf->data(tv->currentIndex().siblingAtColumn(3), Qt::ItemDataRole::DisplayRole).toLongLong(); auto &g = current_group; g.title = tetitle->text().toStdString(); g.author = teauthor->text().toStdString(); g.hc = sbh->value(); g.vc = sbv->value(); l->set_group(curgid, g); refresh_list(); dirty = false; } void group_view::refresh_list() { int64_t curgid = -1; if (tv->currentIndex().isValid() && tv->currentIndex().siblingAtColumn(3).isValid()) { auto idx = tv->currentIndex().siblingAtColumn(3); curgid = mf->data(idx, Qt::ItemDataRole::DisplayRole).toLongLong(); } m->clear(); m->setHorizontalHeaderLabels({"Title", "Author(s)", "Dimension", "id"}); tv->setColumnHidden(3, true); auto gids = l->groups(); auto idx = QModelIndex(); for (auto gid : gids) { map_group_t g = l->get_group(gid); QStandardItem *t = new QStandardItem(QString::fromStdString(g.title)); QStandardItem *a = new QStandardItem(QString::fromStdString(g.author)); QStandardItem *d = new QStandardItem(QString("%1x%2").arg(g.hc).arg(g.vc)); QStandardItem *i = new QStandardItem(); i->setData(QVariant((qlonglong)gid), Qt::ItemDataRole::DisplayRole); m->appendRow({t, a, d, i}); if (gid == curgid) idx = mf->mapFromSource(t->index()); } if (idx.isValid()) { tv->setCurrentIndex(idx); tv->scrollTo(idx); } tv->resizeColumnsToContents(); } void group_view::reset_dim() { p->set_dimension(sbh->value(), sbv->value()); if (!tv->currentIndex().isValid()) return; auto &g = current_group; g.hc = sbh->value(); g.vc = sbv->value(); g.ids.resize(g.hc * g.vc); g.populated.resize(g.hc * g.vc); std::fill(g.populated.begin(), g.populated.end(), false); dirty = true; } void group_view::update_map_view() { auto &g = current_group; p->set_dimension(g.hc, g.vc); for (int i = 0; i < g.hc * g.vc; ++i) p->set_map_id(i, g.populated[i], g.ids[i]); } void group_view::export_current_group() { int64_t curgid = -1; if (tv->currentIndex().isValid() && tv->currentIndex().siblingAtColumn(3).isValid()) { auto idx = tv->currentIndex().siblingAtColumn(3); curgid = mf->data(idx, Qt::ItemDataRole::DisplayRole).toLongLong(); } if (!~curgid) return; QString fn = QFileDialog::getSaveFileName(this, "Export Image", QString(), "Images (*.png)"); if (fn.isEmpty()) return; export_group_image(fn, curgid); } void group_view::export_all_groups() { QString fp = QFileDialog::getExistingDirectory(this, "Select Output Directory"); if (fp.isEmpty()) return; auto gids = l->groups(); for (auto gid : gids) { auto g = l->get_group(gid); auto fn = QString("%1/%2_%3.png").arg(fp).arg(gid).arg(QString::fromStdString(g.title)); export_group_image(fn, gid); } }