mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-12 00:54:53 -07:00
Major Directory Refactoring (#5118)
* refactored cardzone.cpp, added doc and changed if to switch case * started moving every files into different folders * remove undercase to match with other files naming convention * refactored dialog files * ran format.sh * refactored client/tabs folder * refactored client/tabs folder * refactored client/tabs folder * refactored client folder * refactored carddbparser * refactored dialogs * Create sonar-project.properties temporary file for lint * Create build.yml temporary file for lint * removed all files from root directory * removed all files from root directory * added current branch to workflow * fixed most broken import * fixed issues while renaming files * fixed oracle importer * fixed dbconverter * updated translations * made sub-folders for client * removed linter * removed linter folder * fixed oracle import * revert card_zone documentation * renamed db parser files name and deck_view imports * fixed dlg file issue * ran format file and fixed test file * fixed carddb test files * moved player folder in game * updated translations and format files * fixed peglib import * format cmake files * removing vcpkg to try to add it back later * tried fixing vcpkg file * renamed filter to filters and moved database parser to cards folder * reverted translation files * reverted oracle translated * Update cockatrice/src/dialogs/dlg_register.cpp Co-authored-by: tooomm <tooomm@users.noreply.github.com> * Update cockatrice/src/client/ui/window_main.cpp Co-authored-by: tooomm <tooomm@users.noreply.github.com> * removed empty line at file start * removed useless include from tab_supervisor.cpp * refactored cardzone.cpp, added doc and changed if to switch case * started moving every files into different folders * remove undercase to match with other files naming convention * refactored dialog files * ran format.sh * refactored client/tabs folder * refactored client folder * refactored carddbparser * refactored dialogs * removed all files from root directory * Create sonar-project.properties temporary file for lint * Create build.yml temporary file for lint * added current branch to workflow * fixed most broken import * fixed issues while renaming files * fixed oracle importer * fixed dbconverter * updated translations * made sub-folders for client * removed linter * removed linter folder * fixed oracle import * revert card_zone documentation * renamed db parser files name and deck_view imports * fixed dlg file issue * ran format file and fixed test file * fixed carddb test files * moved player folder in game * updated translations and format files * fixed peglib import * reverted translation files * format cmake files * removing vcpkg to try to add it back later * tried fixing vcpkg file * pre-updating of cockatrice changes * removed empty line at file start * reverted oracle translated * Update cockatrice/src/dialogs/dlg_register.cpp Co-authored-by: tooomm <tooomm@users.noreply.github.com> * Update cockatrice/src/client/ui/window_main.cpp Co-authored-by: tooomm <tooomm@users.noreply.github.com> * removed useless include from tab_supervisor.cpp --------- Co-authored-by: tooomm <tooomm@users.noreply.github.com>
This commit is contained in:
parent
d1e0f9dfc5
commit
fa999880ee
261 changed files with 735 additions and 729 deletions
71
cockatrice/src/deck/custom_line_edit.cpp
Normal file
71
cockatrice/src/deck/custom_line_edit.cpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#include "custom_line_edit.h"
|
||||
|
||||
#include "../settings/cache_settings.h"
|
||||
#include "../settings/shortcuts_settings.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QLineEdit>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
|
||||
LineEditUnfocusable::LineEditUnfocusable(QWidget *parent) : QLineEdit(parent)
|
||||
{
|
||||
installEventFilter(this);
|
||||
}
|
||||
|
||||
LineEditUnfocusable::LineEditUnfocusable(const QString &contents, QWidget *parent) : QLineEdit(contents, parent)
|
||||
{
|
||||
installEventFilter(this);
|
||||
}
|
||||
|
||||
bool LineEditUnfocusable::isUnfocusShortcut(QKeyEvent *event)
|
||||
{
|
||||
QString modifier;
|
||||
QString keyNoMod;
|
||||
|
||||
if (event->modifiers() & Qt::ShiftModifier)
|
||||
modifier += "Shift+";
|
||||
if (event->modifiers() & Qt::ControlModifier)
|
||||
modifier += "Ctrl+";
|
||||
if (event->modifiers() & Qt::AltModifier)
|
||||
modifier += "Alt+";
|
||||
if (event->modifiers() & Qt::MetaModifier)
|
||||
modifier += "Meta+";
|
||||
|
||||
keyNoMod = QKeySequence(event->key()).toString();
|
||||
|
||||
QKeySequence key(modifier + keyNoMod);
|
||||
QList<QKeySequence> unfocusShortcut = SettingsCache::instance().shortcuts().getShortcut("Textbox/unfocusTextBox");
|
||||
|
||||
for (const auto &unfocusKey : unfocusShortcut) {
|
||||
if (key.matches(unfocusKey) == QKeySequence::ExactMatch)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LineEditUnfocusable::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (isUnfocusShortcut(event)) {
|
||||
clearFocus();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
QLineEdit::keyPressEvent(event);
|
||||
}
|
||||
|
||||
bool LineEditUnfocusable::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::ShortcutOverride) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
|
||||
if (isUnfocusShortcut(keyEvent)) {
|
||||
event->accept();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return QLineEdit::eventFilter(watched, event);
|
||||
}
|
||||
28
cockatrice/src/deck/custom_line_edit.h
Normal file
28
cockatrice/src/deck/custom_line_edit.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
#ifndef CUSTOMLINEEDIT_H
|
||||
#define CUSTOMLINEEDIT_H
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
class QKeyEvent;
|
||||
class QWidget;
|
||||
class QString;
|
||||
|
||||
// Should be used when the there is a risk of conflict between line editor
|
||||
// shortcuts and other shortcuts
|
||||
class LineEditUnfocusable : public QLineEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LineEditUnfocusable(QWidget *parent = nullptr);
|
||||
explicit LineEditUnfocusable(const QString &contents, QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
static bool isUnfocusShortcut(QKeyEvent *key);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
520
cockatrice/src/deck/deck_list_model.cpp
Normal file
520
cockatrice/src/deck/deck_list_model.cpp
Normal file
|
|
@ -0,0 +1,520 @@
|
|||
#include "deck_list_model.h"
|
||||
|
||||
#include "../game/cards/card_database.h"
|
||||
#include "../main.h"
|
||||
#include "../settings/cache_settings.h"
|
||||
#include "deck_loader.h"
|
||||
|
||||
#include <QBrush>
|
||||
#include <QFile>
|
||||
#include <QFont>
|
||||
#include <QPrinter>
|
||||
#include <QProgressDialog>
|
||||
#include <QTextCursor>
|
||||
#include <QTextDocument>
|
||||
#include <QTextStream>
|
||||
#include <QTextTable>
|
||||
|
||||
DeckListModel::DeckListModel(QObject *parent)
|
||||
: QAbstractItemModel(parent), lastKnownColumn(1), lastKnownOrder(Qt::AscendingOrder)
|
||||
{
|
||||
deckList = new DeckLoader;
|
||||
connect(deckList, SIGNAL(deckLoaded()), this, SLOT(rebuildTree()));
|
||||
connect(deckList, SIGNAL(deckHashChanged()), this, SIGNAL(deckHashChanged()));
|
||||
root = new InnerDecklistNode;
|
||||
}
|
||||
|
||||
DeckListModel::~DeckListModel()
|
||||
{
|
||||
delete root;
|
||||
delete deckList;
|
||||
}
|
||||
|
||||
void DeckListModel::rebuildTree()
|
||||
{
|
||||
beginResetModel();
|
||||
root->clearTree();
|
||||
|
||||
InnerDecklistNode *listRoot = deckList->getRoot();
|
||||
|
||||
for (int i = 0; i < listRoot->size(); i++) {
|
||||
auto *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
||||
auto *node = new InnerDecklistNode(currentZone->getName(), root);
|
||||
|
||||
for (int j = 0; j < currentZone->size(); j++) {
|
||||
auto *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
||||
|
||||
// TODO: better sanity checking
|
||||
if (currentCard == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CardInfoPtr info = db->getCard(currentCard->getName());
|
||||
QString cardType = info ? info->getMainCardType() : "unknown";
|
||||
|
||||
auto *cardTypeNode = dynamic_cast<InnerDecklistNode *>(node->findChild(cardType));
|
||||
|
||||
if (!cardTypeNode) {
|
||||
cardTypeNode = new InnerDecklistNode(cardType, node);
|
||||
}
|
||||
|
||||
new DecklistModelCardNode(currentCard, cardTypeNode);
|
||||
}
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
int DeckListModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
// debugIndexInfo("rowCount", parent);
|
||||
auto *node = getNode<InnerDecklistNode *>(parent);
|
||||
if (node) {
|
||||
return node->size();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int DeckListModel::columnCount(const QModelIndex & /*parent*/) const
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
QVariant DeckListModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
// debugIndexInfo("data", index);
|
||||
if (!index.isValid()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (index.column() >= columnCount()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
auto *temp = static_cast<AbstractDecklistNode *>(index.internalPointer());
|
||||
auto *card = dynamic_cast<DecklistModelCardNode *>(temp);
|
||||
if (card == nullptr) {
|
||||
auto *node = dynamic_cast<InnerDecklistNode *>(temp);
|
||||
switch (role) {
|
||||
case Qt::FontRole: {
|
||||
QFont f;
|
||||
f.setBold(true);
|
||||
return f;
|
||||
}
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole: {
|
||||
switch (index.column()) {
|
||||
case 0:
|
||||
return node->recursiveCount(true);
|
||||
case 1:
|
||||
if (role == Qt::DisplayRole)
|
||||
return node->getVisibleName();
|
||||
else
|
||||
return node->getName();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
case Qt::BackgroundRole: {
|
||||
int color = 90 + 60 * node->depth();
|
||||
return QBrush(QColor(color, 255, color));
|
||||
}
|
||||
case Qt::ForegroundRole: {
|
||||
return QBrush(QColor(0, 0, 0));
|
||||
}
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
} else {
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole: {
|
||||
switch (index.column()) {
|
||||
case 0:
|
||||
return card->getNumber();
|
||||
case 1:
|
||||
return card->getName();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
case Qt::BackgroundRole: {
|
||||
int color = 255 - (index.row() % 2) * 30;
|
||||
return QBrush(QColor(color, color, color));
|
||||
}
|
||||
case Qt::ForegroundRole: {
|
||||
return QBrush(QColor(0, 0, 0));
|
||||
}
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant DeckListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if ((role != Qt::DisplayRole) || (orientation != Qt::Horizontal)) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (section >= columnCount()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
switch (section) {
|
||||
case 0:
|
||||
return tr("Number");
|
||||
case 1:
|
||||
return tr("Card");
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex DeckListModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
// debugIndexInfo("index", parent);
|
||||
if (!hasIndex(row, column, parent)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto *parentNode = getNode<InnerDecklistNode *>(parent);
|
||||
return row >= parentNode->size() ? QModelIndex() : createIndex(row, column, parentNode->at(row));
|
||||
}
|
||||
|
||||
QModelIndex DeckListModel::parent(const QModelIndex &ind) const
|
||||
{
|
||||
if (!ind.isValid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return nodeToIndex(static_cast<AbstractDecklistNode *>(ind.internalPointer())->getParent());
|
||||
}
|
||||
|
||||
Qt::ItemFlags DeckListModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return Qt::NoItemFlags;
|
||||
}
|
||||
|
||||
Qt::ItemFlags result = Qt::ItemIsEnabled;
|
||||
result |= Qt::ItemIsSelectable;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DeckListModel::emitRecursiveUpdates(const QModelIndex &index)
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit dataChanged(index, index);
|
||||
emitRecursiveUpdates(index.parent());
|
||||
}
|
||||
|
||||
bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
auto *node = getNode<DecklistModelCardNode *>(index);
|
||||
if (!node || (role != Qt::EditRole)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (index.column()) {
|
||||
case 0:
|
||||
node->setNumber(value.toInt());
|
||||
break;
|
||||
case 1:
|
||||
node->setName(value.toString());
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
emitRecursiveUpdates(index);
|
||||
deckList->updateDeckHash();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeckListModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
auto *node = getNode<InnerDecklistNode *>(parent);
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (row + count > node->size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
beginRemoveRows(parent, row, row + count - 1);
|
||||
for (int i = 0; i < count; i++) {
|
||||
AbstractDecklistNode *toDelete = node->takeAt(row);
|
||||
if (auto *temp = dynamic_cast<DecklistModelCardNode *>(toDelete)) {
|
||||
deckList->deleteNode(temp->getDataNode());
|
||||
}
|
||||
delete toDelete;
|
||||
}
|
||||
endRemoveRows();
|
||||
|
||||
if (node->empty() && (node != root)) {
|
||||
removeRows(parent.row(), 1, parent.parent());
|
||||
} else {
|
||||
emitRecursiveUpdates(parent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
InnerDecklistNode *DeckListModel::createNodeIfNeeded(const QString &name, InnerDecklistNode *parent)
|
||||
{
|
||||
auto *newNode = dynamic_cast<InnerDecklistNode *>(parent->findChild(name));
|
||||
if (!newNode) {
|
||||
beginInsertRows(nodeToIndex(parent), parent->size(), parent->size());
|
||||
newNode = new InnerDecklistNode(name, parent);
|
||||
endInsertRows();
|
||||
}
|
||||
return newNode;
|
||||
}
|
||||
|
||||
DecklistModelCardNode *DeckListModel::findCardNode(const QString &cardName, const QString &zoneName) const
|
||||
{
|
||||
InnerDecklistNode *zoneNode, *typeNode;
|
||||
CardInfoPtr info;
|
||||
QString cardType;
|
||||
|
||||
zoneNode = dynamic_cast<InnerDecklistNode *>(root->findChild(zoneName));
|
||||
if (!zoneNode) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
info = db->getCard(cardName);
|
||||
if (!info) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cardType = info->getMainCardType();
|
||||
typeNode = dynamic_cast<InnerDecklistNode *>(zoneNode->findChild(cardType));
|
||||
if (!typeNode) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dynamic_cast<DecklistModelCardNode *>(typeNode->findChild(cardName));
|
||||
}
|
||||
|
||||
QModelIndex DeckListModel::findCard(const QString &cardName, const QString &zoneName) const
|
||||
{
|
||||
DecklistModelCardNode *cardNode;
|
||||
|
||||
cardNode = findCardNode(cardName, zoneName);
|
||||
if (!cardNode) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return nodeToIndex(cardNode);
|
||||
}
|
||||
|
||||
QModelIndex DeckListModel::addCard(const QString &cardName, const QString &zoneName, bool abAddAnyway)
|
||||
{
|
||||
CardInfoPtr info = db->getCard(cardName);
|
||||
if (info == nullptr) {
|
||||
if (abAddAnyway) {
|
||||
// We need to keep this card added no matter what
|
||||
// This is usually called from tab_deck_editor
|
||||
// So we'll create a new CardInfo with the name
|
||||
// and default values for all fields
|
||||
info = CardInfo::newInstance(cardName);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
InnerDecklistNode *zoneNode = createNodeIfNeeded(zoneName, root);
|
||||
|
||||
QString cardType = info->getMainCardType();
|
||||
InnerDecklistNode *cardTypeNode = createNodeIfNeeded(cardType, zoneNode);
|
||||
|
||||
QModelIndex parentIndex = nodeToIndex(cardTypeNode);
|
||||
auto *cardNode = dynamic_cast<DecklistModelCardNode *>(cardTypeNode->findChild(cardName));
|
||||
if (!cardNode) {
|
||||
DecklistCardNode *decklistCard = deckList->addCard(cardName, zoneName);
|
||||
beginInsertRows(parentIndex, cardTypeNode->size(), cardTypeNode->size());
|
||||
cardNode = new DecklistModelCardNode(decklistCard, cardTypeNode);
|
||||
endInsertRows();
|
||||
} else {
|
||||
cardNode->setNumber(cardNode->getNumber() + 1);
|
||||
deckList->updateDeckHash();
|
||||
}
|
||||
sort(lastKnownColumn, lastKnownOrder);
|
||||
emitRecursiveUpdates(parentIndex);
|
||||
return nodeToIndex(cardNode);
|
||||
}
|
||||
|
||||
QModelIndex DeckListModel::nodeToIndex(AbstractDecklistNode *node) const
|
||||
{
|
||||
if (node == nullptr || node == root) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return createIndex(node->getParent()->indexOf(node), 0, node);
|
||||
}
|
||||
|
||||
void DeckListModel::sortHelper(InnerDecklistNode *node, Qt::SortOrder order)
|
||||
{
|
||||
// Sort children of node and save the information needed to
|
||||
// update the list of persistent indexes.
|
||||
QVector<QPair<int, int>> sortResult = node->sort(order);
|
||||
|
||||
QModelIndexList from, to;
|
||||
int columns = columnCount();
|
||||
for (int i = sortResult.size() - 1; i >= 0; --i) {
|
||||
const int fromRow = sortResult[i].first;
|
||||
const int toRow = sortResult[i].second;
|
||||
AbstractDecklistNode *temp = node->at(toRow);
|
||||
for (int j = 0; j < columns; ++j) {
|
||||
from << createIndex(fromRow, j, temp);
|
||||
to << createIndex(toRow, j, temp);
|
||||
}
|
||||
}
|
||||
changePersistentIndexList(from, to);
|
||||
|
||||
// Recursion
|
||||
for (int i = node->size() - 1; i >= 0; --i) {
|
||||
auto *subNode = dynamic_cast<InnerDecklistNode *>(node->at(i));
|
||||
if (subNode) {
|
||||
sortHelper(subNode, order);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeckListModel::sort(int column, Qt::SortOrder order)
|
||||
{
|
||||
lastKnownColumn = column;
|
||||
lastKnownOrder = order;
|
||||
|
||||
emit layoutAboutToBeChanged();
|
||||
DeckSortMethod sortMethod;
|
||||
switch (column) {
|
||||
case 0:
|
||||
sortMethod = ByNumber;
|
||||
break;
|
||||
case 1:
|
||||
sortMethod = ByName;
|
||||
break;
|
||||
default:
|
||||
sortMethod = ByName;
|
||||
}
|
||||
|
||||
root->setSortMethod(sortMethod);
|
||||
sortHelper(root, order);
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
||||
void DeckListModel::cleanList()
|
||||
{
|
||||
setDeckList(new DeckLoader);
|
||||
}
|
||||
|
||||
void DeckListModel::setDeckList(DeckLoader *_deck)
|
||||
{
|
||||
delete deckList;
|
||||
deckList = _deck;
|
||||
connect(deckList, SIGNAL(deckLoaded()), this, SLOT(rebuildTree()));
|
||||
connect(deckList, SIGNAL(deckHashChanged()), this, SIGNAL(deckHashChanged()));
|
||||
rebuildTree();
|
||||
}
|
||||
|
||||
void DeckListModel::printDeckListNode(QTextCursor *cursor, InnerDecklistNode *node)
|
||||
{
|
||||
const int totalColumns = 2;
|
||||
|
||||
if (node->height() == 1) {
|
||||
QTextBlockFormat blockFormat;
|
||||
QTextCharFormat charFormat;
|
||||
charFormat.setFontPointSize(11);
|
||||
charFormat.setFontWeight(QFont::Bold);
|
||||
cursor->insertBlock(blockFormat, charFormat);
|
||||
|
||||
QTextTableFormat tableFormat;
|
||||
tableFormat.setCellPadding(0);
|
||||
tableFormat.setCellSpacing(0);
|
||||
tableFormat.setBorder(0);
|
||||
QTextTable *table = cursor->insertTable(node->size() + 1, totalColumns, tableFormat);
|
||||
for (int i = 0; i < node->size(); i++) {
|
||||
auto *card = dynamic_cast<AbstractDecklistCardNode *>(node->at(i));
|
||||
|
||||
QTextCharFormat cellCharFormat;
|
||||
cellCharFormat.setFontPointSize(9);
|
||||
|
||||
QTextTableCell cell = table->cellAt(i, 0);
|
||||
cell.setFormat(cellCharFormat);
|
||||
QTextCursor cellCursor = cell.firstCursorPosition();
|
||||
cellCursor.insertText(QString("%1 ").arg(card->getNumber()));
|
||||
|
||||
cell = table->cellAt(i, 1);
|
||||
cell.setFormat(cellCharFormat);
|
||||
cellCursor = cell.firstCursorPosition();
|
||||
cellCursor.insertText(card->getName());
|
||||
}
|
||||
} else if (node->height() == 2) {
|
||||
QTextBlockFormat blockFormat;
|
||||
QTextCharFormat charFormat;
|
||||
charFormat.setFontPointSize(14);
|
||||
charFormat.setFontWeight(QFont::Bold);
|
||||
|
||||
cursor->insertBlock(blockFormat, charFormat);
|
||||
|
||||
QTextTableFormat tableFormat;
|
||||
tableFormat.setCellPadding(10);
|
||||
tableFormat.setCellSpacing(0);
|
||||
tableFormat.setBorder(0);
|
||||
QVector<QTextLength> constraints;
|
||||
for (int i = 0; i < totalColumns; i++) {
|
||||
constraints << QTextLength(QTextLength::PercentageLength, 100.0 / totalColumns);
|
||||
}
|
||||
tableFormat.setColumnWidthConstraints(constraints);
|
||||
|
||||
QTextTable *table = cursor->insertTable(1, totalColumns, tableFormat);
|
||||
for (int i = 0; i < node->size(); i++) {
|
||||
QTextCursor cellCursor = table->cellAt(0, (i * totalColumns) / node->size()).lastCursorPosition();
|
||||
printDeckListNode(&cellCursor, dynamic_cast<InnerDecklistNode *>(node->at(i)));
|
||||
}
|
||||
}
|
||||
|
||||
cursor->movePosition(QTextCursor::End);
|
||||
}
|
||||
|
||||
void DeckListModel::printDeckList(QPrinter *printer)
|
||||
{
|
||||
QTextDocument doc;
|
||||
|
||||
QFont font("Serif");
|
||||
font.setStyleHint(QFont::Serif);
|
||||
doc.setDefaultFont(font);
|
||||
|
||||
QTextCursor cursor(&doc);
|
||||
|
||||
QTextBlockFormat headerBlockFormat;
|
||||
QTextCharFormat headerCharFormat;
|
||||
headerCharFormat.setFontPointSize(16);
|
||||
headerCharFormat.setFontWeight(QFont::Bold);
|
||||
|
||||
cursor.insertBlock(headerBlockFormat, headerCharFormat);
|
||||
cursor.insertText(deckList->getName());
|
||||
|
||||
headerCharFormat.setFontPointSize(12);
|
||||
cursor.insertBlock(headerBlockFormat, headerCharFormat);
|
||||
cursor.insertText(deckList->getComments());
|
||||
cursor.insertBlock(headerBlockFormat, headerCharFormat);
|
||||
|
||||
for (int i = 0; i < root->size(); i++) {
|
||||
cursor.insertHtml("<br><img src=theme:hr.jpg>");
|
||||
// cursor.insertHtml("<hr>");
|
||||
cursor.insertBlock(headerBlockFormat, headerCharFormat);
|
||||
|
||||
printDeckListNode(&cursor, dynamic_cast<InnerDecklistNode *>(root->at(i)));
|
||||
}
|
||||
|
||||
doc.print(printer);
|
||||
}
|
||||
99
cockatrice/src/deck/deck_list_model.h
Normal file
99
cockatrice/src/deck/deck_list_model.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#ifndef DECKLISTMODEL_H
|
||||
#define DECKLISTMODEL_H
|
||||
|
||||
#include "decklist.h"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QList>
|
||||
|
||||
class DeckLoader;
|
||||
class CardDatabase;
|
||||
class QPrinter;
|
||||
class QTextCursor;
|
||||
|
||||
class DecklistModelCardNode : public AbstractDecklistCardNode
|
||||
{
|
||||
private:
|
||||
DecklistCardNode *dataNode;
|
||||
|
||||
public:
|
||||
DecklistModelCardNode(DecklistCardNode *_dataNode, InnerDecklistNode *_parent)
|
||||
: AbstractDecklistCardNode(_parent), dataNode(_dataNode)
|
||||
{
|
||||
}
|
||||
int getNumber() const override
|
||||
{
|
||||
return dataNode->getNumber();
|
||||
}
|
||||
void setNumber(int _number) override
|
||||
{
|
||||
dataNode->setNumber(_number);
|
||||
}
|
||||
QString getName() const override
|
||||
{
|
||||
return dataNode->getName();
|
||||
}
|
||||
void setName(const QString &_name) override
|
||||
{
|
||||
dataNode->setName(_name);
|
||||
}
|
||||
DecklistCardNode *getDataNode() const
|
||||
{
|
||||
return dataNode;
|
||||
}
|
||||
};
|
||||
|
||||
class DeckListModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void rebuildTree();
|
||||
public slots:
|
||||
void printDeckList(QPrinter *printer);
|
||||
signals:
|
||||
void deckHashChanged();
|
||||
|
||||
public:
|
||||
explicit DeckListModel(QObject *parent = nullptr);
|
||||
~DeckListModel() override;
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
|
||||
QModelIndex parent(const QModelIndex &index) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
bool removeRows(int row, int count, const QModelIndex &parent) override;
|
||||
QModelIndex findCard(const QString &cardName, const QString &zoneName) const;
|
||||
QModelIndex addCard(const QString &cardName, const QString &zoneName, bool abAddAnyway = false);
|
||||
void sort(int column, Qt::SortOrder order) override;
|
||||
void cleanList();
|
||||
DeckLoader *getDeckList() const
|
||||
{
|
||||
return deckList;
|
||||
}
|
||||
void setDeckList(DeckLoader *_deck);
|
||||
|
||||
private:
|
||||
DeckLoader *deckList;
|
||||
InnerDecklistNode *root;
|
||||
int lastKnownColumn;
|
||||
Qt::SortOrder lastKnownOrder;
|
||||
InnerDecklistNode *createNodeIfNeeded(const QString &name, InnerDecklistNode *parent);
|
||||
QModelIndex nodeToIndex(AbstractDecklistNode *node) const;
|
||||
DecklistModelCardNode *findCardNode(const QString &cardName, const QString &zoneName) const;
|
||||
void emitRecursiveUpdates(const QModelIndex &index);
|
||||
void sortHelper(InnerDecklistNode *node, Qt::SortOrder order);
|
||||
|
||||
void printDeckListNode(QTextCursor *cursor, InnerDecklistNode *node);
|
||||
|
||||
template <typename T> T getNode(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return dynamic_cast<T>(root);
|
||||
return dynamic_cast<T>(static_cast<AbstractDecklistNode *>(index.internalPointer()));
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
302
cockatrice/src/deck/deck_loader.cpp
Normal file
302
cockatrice/src/deck/deck_loader.cpp
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
#include "deck_loader.h"
|
||||
|
||||
#include "../game/cards/card_database.h"
|
||||
#include "../main.h"
|
||||
#include "decklist.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QRegularExpression>
|
||||
#include <QStringList>
|
||||
|
||||
const QStringList DeckLoader::fileNameFilters = QStringList()
|
||||
<< QObject::tr("Common deck formats (*.cod *.dec *.dek *.txt *.mwDeck)")
|
||||
<< QObject::tr("All files (*.*)");
|
||||
|
||||
DeckLoader::DeckLoader() : DeckList(), lastFileName(QString()), lastFileFormat(CockatriceFormat), lastRemoteDeckId(-1)
|
||||
{
|
||||
}
|
||||
|
||||
DeckLoader::DeckLoader(const QString &nativeString)
|
||||
: DeckList(nativeString), lastFileName(QString()), lastFileFormat(CockatriceFormat), lastRemoteDeckId(-1)
|
||||
{
|
||||
}
|
||||
|
||||
DeckLoader::DeckLoader(const DeckList &other)
|
||||
: DeckList(other), lastFileName(QString()), lastFileFormat(CockatriceFormat), lastRemoteDeckId(-1)
|
||||
{
|
||||
}
|
||||
|
||||
DeckLoader::DeckLoader(const DeckLoader &other)
|
||||
: DeckList(other), lastFileName(other.lastFileName), lastFileFormat(other.lastFileFormat),
|
||||
lastRemoteDeckId(other.lastRemoteDeckId)
|
||||
{
|
||||
}
|
||||
|
||||
bool DeckLoader::loadFromFile(const QString &fileName, FileFormat fmt)
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
switch (fmt) {
|
||||
case PlainTextFormat:
|
||||
result = loadFromFile_Plain(&file);
|
||||
break;
|
||||
case CockatriceFormat: {
|
||||
result = loadFromFile_Native(&file);
|
||||
qDebug() << "Loaded from" << fileName << "-" << result;
|
||||
if (!result) {
|
||||
qDebug() << "Retying as plain format";
|
||||
file.seek(0);
|
||||
result = loadFromFile_Plain(&file);
|
||||
fmt = PlainTextFormat;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
lastFileName = fileName;
|
||||
lastFileFormat = fmt;
|
||||
|
||||
emit deckLoaded();
|
||||
}
|
||||
|
||||
qDebug() << "Deck was loaded -" << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DeckLoader::loadFromRemote(const QString &nativeString, int remoteDeckId)
|
||||
{
|
||||
bool result = loadFromString_Native(nativeString);
|
||||
if (result) {
|
||||
lastFileName = QString();
|
||||
lastFileFormat = CockatriceFormat;
|
||||
lastRemoteDeckId = remoteDeckId;
|
||||
|
||||
emit deckLoaded();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DeckLoader::saveToFile(const QString &fileName, FileFormat fmt)
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
switch (fmt) {
|
||||
case PlainTextFormat:
|
||||
result = saveToFile_Plain(&file);
|
||||
break;
|
||||
case CockatriceFormat:
|
||||
result = saveToFile_Native(&file);
|
||||
break;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
lastFileName = fileName;
|
||||
lastFileFormat = fmt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// This struct is here to support the forEachCard function call, defined in decklist. It
|
||||
// requires a function to be called for each card, and passes an inner node and a card for
|
||||
// each card in the decklist.
|
||||
struct FormatDeckListForExport
|
||||
{
|
||||
// Create refrences for the strings that will be passed in.
|
||||
QString &mainBoardCards;
|
||||
QString &sideBoardCards;
|
||||
// create main operator for struct, allowing the foreachcard to work.
|
||||
FormatDeckListForExport(QString &_mainBoardCards, QString &_sideBoardCards)
|
||||
: mainBoardCards(_mainBoardCards), sideBoardCards(_sideBoardCards){};
|
||||
|
||||
void operator()(const InnerDecklistNode *node, const DecklistCardNode *card) const
|
||||
{
|
||||
// Get the card name
|
||||
CardInfoPtr dbCard = db->getCard(card->getName());
|
||||
if (!dbCard || dbCard->getIsToken()) {
|
||||
// If it's a token, we don't care about the card.
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it's a sideboard card.
|
||||
if (node->getName() == DECK_ZONE_SIDE) {
|
||||
// Get the number of cards and add the card name
|
||||
sideBoardCards += QString::number(card->getNumber());
|
||||
// Add a space between card num and name
|
||||
sideBoardCards += "%20";
|
||||
// Add card name
|
||||
sideBoardCards += card->getName();
|
||||
// Add a return at the end of the card
|
||||
sideBoardCards += "%0A";
|
||||
} else // If it's a mainboard card, do the same thing, but for the mainboard card string
|
||||
{
|
||||
mainBoardCards += QString::number(card->getNumber());
|
||||
mainBoardCards += "%20";
|
||||
mainBoardCards += card->getName();
|
||||
mainBoardCards += "%0A";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Export deck to decklist function, called to format the deck in a way to be sent to a server
|
||||
QString DeckLoader::exportDeckToDecklist()
|
||||
{
|
||||
// Add the base url
|
||||
QString deckString = "https://www.decklist.org/?";
|
||||
// Create two strings to pass to function
|
||||
QString mainBoardCards, sideBoardCards;
|
||||
// Set up the struct to call.
|
||||
FormatDeckListForExport formatDeckListForExport(mainBoardCards, sideBoardCards);
|
||||
// call our struct function for each card in the deck
|
||||
forEachCard(formatDeckListForExport);
|
||||
// Remove the extra return at the end of the last cards
|
||||
mainBoardCards.chop(3);
|
||||
sideBoardCards.chop(3);
|
||||
// if after we've called it for each card, and the strings are empty, we know that
|
||||
// there were no non-token cards in the deck, so show an error message.
|
||||
if ((QString::compare(mainBoardCards, "", Qt::CaseInsensitive) == 0) &&
|
||||
(QString::compare(sideBoardCards, "", Qt::CaseInsensitive) == 0)) {
|
||||
return "";
|
||||
}
|
||||
// return a string with the url for decklist export
|
||||
deckString += "deckmain=" + mainBoardCards + "&deckside=" + sideBoardCards;
|
||||
return deckString;
|
||||
}
|
||||
|
||||
DeckLoader::FileFormat DeckLoader::getFormatFromName(const QString &fileName)
|
||||
{
|
||||
if (fileName.endsWith(".cod", Qt::CaseInsensitive)) {
|
||||
return CockatriceFormat;
|
||||
}
|
||||
return PlainTextFormat;
|
||||
}
|
||||
|
||||
bool DeckLoader::saveToStream_Plain(QTextStream &out, bool addComments)
|
||||
{
|
||||
if (addComments) {
|
||||
saveToStream_DeckHeader(out);
|
||||
}
|
||||
|
||||
// loop zones
|
||||
for (int i = 0; i < getRoot()->size(); i++) {
|
||||
const auto *zoneNode = dynamic_cast<InnerDecklistNode *>(getRoot()->at(i));
|
||||
|
||||
saveToStream_DeckZone(out, zoneNode, addComments);
|
||||
|
||||
// end of zone
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeckLoader::saveToStream_DeckHeader(QTextStream &out)
|
||||
{
|
||||
if (!getName().isEmpty()) {
|
||||
out << "// " << getName() << "\n\n";
|
||||
}
|
||||
|
||||
if (!getComments().isEmpty()) {
|
||||
QStringList commentRows = getComments().split(QRegularExpression("\n|\r\n|\r"));
|
||||
foreach (QString row, commentRows) {
|
||||
out << "// " << row << "\n";
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void DeckLoader::saveToStream_DeckZone(QTextStream &out, const InnerDecklistNode *zoneNode, bool addComments)
|
||||
{
|
||||
// group cards by card type and count the subtotals
|
||||
QMultiMap<QString, DecklistCardNode *> cardsByType;
|
||||
QMap<QString, int> cardTotalByType;
|
||||
int cardTotal = 0;
|
||||
|
||||
for (int j = 0; j < zoneNode->size(); j++) {
|
||||
auto *card = dynamic_cast<DecklistCardNode *>(zoneNode->at(j));
|
||||
|
||||
CardInfoPtr info = db->getCard(card->getName());
|
||||
QString cardType = info ? info->getMainCardType() : "unknown";
|
||||
|
||||
cardsByType.insert(cardType, card);
|
||||
|
||||
if (cardTotalByType.contains(cardType)) {
|
||||
cardTotalByType[cardType] += card->getNumber();
|
||||
} else {
|
||||
cardTotalByType[cardType] = card->getNumber();
|
||||
}
|
||||
|
||||
cardTotal += card->getNumber();
|
||||
}
|
||||
|
||||
if (addComments) {
|
||||
out << "// " << cardTotal << " " << zoneNode->getVisibleName() << "\n";
|
||||
}
|
||||
|
||||
// print cards to stream
|
||||
foreach (QString cardType, cardsByType.uniqueKeys()) {
|
||||
if (addComments) {
|
||||
out << "// " << cardTotalByType[cardType] << " " << cardType << "\n";
|
||||
}
|
||||
|
||||
QList<DecklistCardNode *> cards = cardsByType.values(cardType);
|
||||
|
||||
saveToStream_DeckZoneCards(out, zoneNode, cards, addComments);
|
||||
|
||||
if (addComments) {
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeckLoader::saveToStream_DeckZoneCards(QTextStream &out,
|
||||
const InnerDecklistNode *zoneNode,
|
||||
QList<DecklistCardNode *> cards,
|
||||
bool addComments)
|
||||
{
|
||||
// QMultiMap sorts values in reverse order
|
||||
for (int i = cards.size() - 1; i >= 0; --i) {
|
||||
DecklistCardNode *card = cards[i];
|
||||
|
||||
if (zoneNode->getName() == DECK_ZONE_SIDE && addComments) {
|
||||
out << "SB: ";
|
||||
}
|
||||
|
||||
out << card->getNumber() << " " << card->getName() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
QString DeckLoader::getCardZoneFromName(QString cardName, QString currentZoneName)
|
||||
{
|
||||
CardInfoPtr card = db->getCard(cardName);
|
||||
|
||||
if (card && card->getIsToken()) {
|
||||
return DECK_ZONE_TOKENS;
|
||||
}
|
||||
|
||||
return currentZoneName;
|
||||
}
|
||||
|
||||
QString DeckLoader::getCompleteCardName(const QString &cardName) const
|
||||
{
|
||||
if (db) {
|
||||
CardInfoPtr temp = db->guessCard(cardName);
|
||||
if (temp) {
|
||||
return temp->getName();
|
||||
}
|
||||
}
|
||||
|
||||
return cardName;
|
||||
}
|
||||
64
cockatrice/src/deck/deck_loader.h
Normal file
64
cockatrice/src/deck/deck_loader.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#ifndef DECK_LOADER_H
|
||||
#define DECK_LOADER_H
|
||||
|
||||
#include "decklist.h"
|
||||
|
||||
class DeckLoader : public DeckList
|
||||
{
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void deckLoaded();
|
||||
|
||||
public:
|
||||
enum FileFormat
|
||||
{
|
||||
PlainTextFormat,
|
||||
CockatriceFormat
|
||||
};
|
||||
static const QStringList fileNameFilters;
|
||||
|
||||
private:
|
||||
QString lastFileName;
|
||||
FileFormat lastFileFormat;
|
||||
int lastRemoteDeckId;
|
||||
|
||||
public:
|
||||
DeckLoader();
|
||||
DeckLoader(const QString &nativeString);
|
||||
DeckLoader(const DeckList &other);
|
||||
DeckLoader(const DeckLoader &other);
|
||||
const QString &getLastFileName() const
|
||||
{
|
||||
return lastFileName;
|
||||
}
|
||||
FileFormat getLastFileFormat() const
|
||||
{
|
||||
return lastFileFormat;
|
||||
}
|
||||
int getLastRemoteDeckId() const
|
||||
{
|
||||
return lastRemoteDeckId;
|
||||
}
|
||||
|
||||
static FileFormat getFormatFromName(const QString &fileName);
|
||||
|
||||
bool loadFromFile(const QString &fileName, FileFormat fmt);
|
||||
bool loadFromRemote(const QString &nativeString, int remoteDeckId);
|
||||
bool saveToFile(const QString &fileName, FileFormat fmt);
|
||||
QString exportDeckToDecklist();
|
||||
|
||||
// overload
|
||||
bool saveToStream_Plain(QTextStream &out, bool addComments = true);
|
||||
|
||||
protected:
|
||||
void saveToStream_DeckHeader(QTextStream &out);
|
||||
void saveToStream_DeckZone(QTextStream &out, const InnerDecklistNode *zoneNode, bool addComments = true);
|
||||
void saveToStream_DeckZoneCards(QTextStream &out,
|
||||
const InnerDecklistNode *zoneNode,
|
||||
QList<DecklistCardNode *> cards,
|
||||
bool addComments = true);
|
||||
[[nodiscard]] QString getCardZoneFromName(QString cardName, QString currentZoneName) override;
|
||||
[[nodiscard]] QString getCompleteCardName(const QString &cardName) const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
92
cockatrice/src/deck/deck_stats_interface.cpp
Normal file
92
cockatrice/src/deck/deck_stats_interface.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#include "deck_stats_interface.h"
|
||||
|
||||
#include "decklist.h"
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QRegularExpression>
|
||||
#include <QUrlQuery>
|
||||
|
||||
DeckStatsInterface::DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent)
|
||||
: QObject(parent), cardDatabase(_cardDatabase)
|
||||
{
|
||||
manager = new QNetworkAccessManager(this);
|
||||
connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(queryFinished(QNetworkReply *)));
|
||||
}
|
||||
|
||||
void DeckStatsInterface::queryFinished(QNetworkReply *reply)
|
||||
{
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
QMessageBox::critical(nullptr, tr("Error"), reply->errorString());
|
||||
reply->deleteLater();
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QString data(reply->readAll());
|
||||
reply->deleteLater();
|
||||
|
||||
static const QRegularExpression rx("<meta property=\"og:url\" content=\"([^\"]+)\"");
|
||||
auto match = rx.match(data);
|
||||
if (!match.hasMatch()) {
|
||||
QMessageBox::critical(nullptr, tr("Error"), tr("The reply from the server could not be parsed."));
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QString deckUrl = match.captured(1);
|
||||
QDesktopServices::openUrl(deckUrl);
|
||||
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void DeckStatsInterface::getAnalyzeRequestData(DeckList *deck, QByteArray *data)
|
||||
{
|
||||
DeckList deckWithoutTokens;
|
||||
copyDeckWithoutTokens(*deck, deckWithoutTokens);
|
||||
|
||||
QUrl params;
|
||||
QUrlQuery urlQuery;
|
||||
urlQuery.addQueryItem("deck", deckWithoutTokens.writeToString_Plain());
|
||||
urlQuery.addQueryItem("decktitle", deck->getName());
|
||||
params.setQuery(urlQuery);
|
||||
data->append(params.query(QUrl::EncodeReserved).toUtf8());
|
||||
}
|
||||
|
||||
void DeckStatsInterface::analyzeDeck(DeckList *deck)
|
||||
{
|
||||
QByteArray data;
|
||||
getAnalyzeRequestData(deck, &data);
|
||||
|
||||
QNetworkRequest request(QUrl("https://deckstats.net/index.php"));
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
|
||||
manager->post(request, data);
|
||||
}
|
||||
|
||||
struct CopyIfNotAToken
|
||||
{
|
||||
CardDatabase &cardDatabase;
|
||||
DeckList &destination;
|
||||
|
||||
CopyIfNotAToken(CardDatabase &_cardDatabase, DeckList &_destination)
|
||||
: cardDatabase(_cardDatabase), destination(_destination){};
|
||||
|
||||
void operator()(const InnerDecklistNode *node, const DecklistCardNode *card) const
|
||||
{
|
||||
CardInfoPtr dbCard = cardDatabase.getCard(card->getName());
|
||||
if (dbCard && !dbCard->getIsToken()) {
|
||||
DecklistCardNode *addedCard = destination.addCard(card->getName(), node->getName());
|
||||
addedCard->setNumber(card->getNumber());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void DeckStatsInterface::copyDeckWithoutTokens(const DeckList &source, DeckList &destination)
|
||||
{
|
||||
CopyIfNotAToken copyIfNotAToken(cardDatabase, destination);
|
||||
source.forEachCard(copyIfNotAToken);
|
||||
}
|
||||
38
cockatrice/src/deck/deck_stats_interface.h
Normal file
38
cockatrice/src/deck/deck_stats_interface.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef DECKSTATS_INTERFACE_H
|
||||
#define DECKSTATS_INTERFACE_H
|
||||
|
||||
#include "../game/cards/card_database.h"
|
||||
#include "decklist.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QByteArray;
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class DeckList;
|
||||
|
||||
class DeckStatsInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
QNetworkAccessManager *manager;
|
||||
|
||||
CardDatabase &cardDatabase;
|
||||
|
||||
/**
|
||||
* Deckstats doesn't recognize token cards, and instead tries to find the
|
||||
* closest non-token card instead. So we construct a new deck which has no
|
||||
* tokens.
|
||||
*/
|
||||
void copyDeckWithoutTokens(const DeckList &source, DeckList &destination);
|
||||
|
||||
private slots:
|
||||
void queryFinished(QNetworkReply *reply);
|
||||
void getAnalyzeRequestData(DeckList *deck, QByteArray *data);
|
||||
|
||||
public:
|
||||
DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent = nullptr);
|
||||
void analyzeDeck(DeckList *deck);
|
||||
};
|
||||
|
||||
#endif
|
||||
520
cockatrice/src/deck/deck_view.cpp
Normal file
520
cockatrice/src/deck/deck_view.cpp
Normal file
|
|
@ -0,0 +1,520 @@
|
|||
#include "deck_view.h"
|
||||
|
||||
#include "../client/ui/theme_manager.h"
|
||||
#include "../game/cards/card_database.h"
|
||||
#include "../main.h"
|
||||
#include "../settings/cache_settings.h"
|
||||
#include "decklist.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QtMath>
|
||||
#include <algorithm>
|
||||
|
||||
DeckViewCardDragItem::DeckViewCardDragItem(DeckViewCard *_item,
|
||||
const QPointF &_hotSpot,
|
||||
AbstractCardDragItem *parentDrag)
|
||||
: AbstractCardDragItem(_item, _hotSpot, parentDrag), currentZone(0)
|
||||
{
|
||||
}
|
||||
|
||||
void DeckViewCardDragItem::updatePosition(const QPointF &cursorScenePos)
|
||||
{
|
||||
QList<QGraphicsItem *> colliding = scene()->items(cursorScenePos);
|
||||
|
||||
DeckViewCardContainer *cursorZone = 0;
|
||||
for (int i = colliding.size() - 1; i >= 0; i--)
|
||||
if ((cursorZone = qgraphicsitem_cast<DeckViewCardContainer *>(colliding.at(i))))
|
||||
break;
|
||||
if (!cursorZone)
|
||||
return;
|
||||
currentZone = cursorZone;
|
||||
|
||||
QPointF newPos = cursorScenePos;
|
||||
if (newPos != pos()) {
|
||||
for (int i = 0; i < childDrags.size(); i++)
|
||||
childDrags[i]->setPos(newPos + childDrags[i]->getHotSpot());
|
||||
setPos(newPos);
|
||||
}
|
||||
}
|
||||
|
||||
void DeckViewCardDragItem::handleDrop(DeckViewCardContainer *target)
|
||||
{
|
||||
DeckViewCard *card = static_cast<DeckViewCard *>(item);
|
||||
DeckViewCardContainer *start = static_cast<DeckViewCardContainer *>(item->parentItem());
|
||||
start->removeCard(card);
|
||||
target->addCard(card);
|
||||
card->setParentItem(target);
|
||||
}
|
||||
|
||||
void DeckViewCardDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
DeckViewScene *sc = static_cast<DeckViewScene *>(scene());
|
||||
sc->removeItem(this);
|
||||
|
||||
if (currentZone) {
|
||||
handleDrop(currentZone);
|
||||
for (int i = 0; i < childDrags.size(); i++) {
|
||||
DeckViewCardDragItem *c = static_cast<DeckViewCardDragItem *>(childDrags[i]);
|
||||
c->handleDrop(currentZone);
|
||||
sc->removeItem(c);
|
||||
}
|
||||
|
||||
sc->updateContents();
|
||||
}
|
||||
|
||||
event->accept();
|
||||
}
|
||||
|
||||
DeckViewCard::DeckViewCard(const QString &_name, const QString &_originZone, QGraphicsItem *parent)
|
||||
: AbstractCardItem(_name, 0, -1, parent), originZone(_originZone), dragItem(0)
|
||||
{
|
||||
setAcceptHoverEvents(true);
|
||||
}
|
||||
|
||||
DeckViewCard::~DeckViewCard()
|
||||
{
|
||||
delete dragItem;
|
||||
}
|
||||
|
||||
void DeckViewCard::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
AbstractCardItem::paint(painter, option, widget);
|
||||
|
||||
painter->save();
|
||||
QPen pen;
|
||||
pen.setWidth(3);
|
||||
pen.setJoinStyle(Qt::MiterJoin);
|
||||
pen.setColor(originZone == DECK_ZONE_MAIN ? Qt::green : Qt::red);
|
||||
painter->setPen(pen);
|
||||
qreal cardRadius = 0.05 * (CARD_WIDTH - 3);
|
||||
painter->drawRoundedRect(QRectF(1.5, 1.5, CARD_WIDTH - 3., CARD_HEIGHT - 3.), cardRadius, cardRadius);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void DeckViewCard::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if ((event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton)).manhattanLength() <
|
||||
2 * QApplication::startDragDistance())
|
||||
return;
|
||||
|
||||
if (static_cast<DeckViewScene *>(scene())->getLocked())
|
||||
return;
|
||||
|
||||
delete dragItem;
|
||||
dragItem = new DeckViewCardDragItem(this, event->pos());
|
||||
scene()->addItem(dragItem);
|
||||
dragItem->updatePosition(event->scenePos());
|
||||
dragItem->grabMouse();
|
||||
|
||||
QList<QGraphicsItem *> sel = scene()->selectedItems();
|
||||
int j = 0;
|
||||
for (int i = 0; i < sel.size(); i++) {
|
||||
DeckViewCard *c = static_cast<DeckViewCard *>(sel.at(i));
|
||||
if (c == this)
|
||||
continue;
|
||||
++j;
|
||||
QPointF childPos = QPointF(j * CARD_WIDTH / 2, 0);
|
||||
DeckViewCardDragItem *drag = new DeckViewCardDragItem(c, childPos, dragItem);
|
||||
drag->setPos(dragItem->pos() + childPos);
|
||||
scene()->addItem(drag);
|
||||
}
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
}
|
||||
|
||||
void DeckView::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
if (static_cast<DeckViewScene *>(scene())->getLocked())
|
||||
return;
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
QList<MoveCard_ToZone> result;
|
||||
QList<QGraphicsItem *> sel = scene()->selectedItems();
|
||||
|
||||
for (int i = 0; i < sel.size(); i++) {
|
||||
DeckViewCard *c = static_cast<DeckViewCard *>(sel.at(i));
|
||||
DeckViewCardContainer *zone = static_cast<DeckViewCardContainer *>(c->parentItem());
|
||||
MoveCard_ToZone m;
|
||||
m.set_card_name(c->getName().toStdString());
|
||||
m.set_start_zone(zone->getName().toStdString());
|
||||
|
||||
if (zone->getName() == DECK_ZONE_MAIN)
|
||||
m.set_target_zone(DECK_ZONE_SIDE);
|
||||
else if (zone->getName() == DECK_ZONE_SIDE)
|
||||
m.set_target_zone(DECK_ZONE_MAIN);
|
||||
else // Trying to move from another zone
|
||||
m.set_target_zone(zone->getName().toStdString());
|
||||
|
||||
result.append(m);
|
||||
}
|
||||
|
||||
deckViewScene->applySideboardPlan(result);
|
||||
deckViewScene->rearrangeItems();
|
||||
emit deckViewScene->sideboardPlanChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void DeckViewCard::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
processHoverEvent();
|
||||
}
|
||||
|
||||
DeckViewCardContainer::DeckViewCardContainer(const QString &_name) : QGraphicsItem(), name(_name), width(0), height(0)
|
||||
{
|
||||
setCacheMode(DeviceCoordinateCache);
|
||||
}
|
||||
|
||||
QRectF DeckViewCardContainer::boundingRect() const
|
||||
{
|
||||
return QRectF(0, 0, width, height);
|
||||
}
|
||||
|
||||
void DeckViewCardContainer::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
||||
{
|
||||
qreal totalTextWidth = getCardTypeTextWidth();
|
||||
|
||||
painter->fillRect(boundingRect(), themeManager->getTableBgBrush());
|
||||
painter->setPen(QColor(255, 255, 255, 100));
|
||||
painter->drawLine(QPointF(0, separatorY), QPointF(width, separatorY));
|
||||
|
||||
painter->setPen(QColor(Qt::white));
|
||||
QFont f("Serif");
|
||||
f.setStyleHint(QFont::Serif);
|
||||
f.setPixelSize(24);
|
||||
f.setWeight(QFont::Bold);
|
||||
painter->setFont(f);
|
||||
painter->drawText(10, 0, width - 20, separatorY, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine,
|
||||
InnerDecklistNode::visibleNameFromName(name) + QString(": %1").arg(cards.size()));
|
||||
|
||||
f.setPixelSize(16);
|
||||
painter->setFont(f);
|
||||
QList<QString> cardTypeList = cardsByType.uniqueKeys();
|
||||
qreal yUntilNow = separatorY + paddingY;
|
||||
for (int i = 0; i < cardTypeList.size(); ++i) {
|
||||
if (i != 0) {
|
||||
painter->setPen(QColor(255, 255, 255, 100));
|
||||
painter->drawLine(QPointF(0, yUntilNow - paddingY / 2), QPointF(width, yUntilNow - paddingY / 2));
|
||||
}
|
||||
qreal thisRowHeight = CARD_HEIGHT * currentRowsAndCols[i].first;
|
||||
QRectF textRect(0, yUntilNow, totalTextWidth, thisRowHeight);
|
||||
yUntilNow += thisRowHeight + paddingY;
|
||||
|
||||
QString displayString = QString("%1\n(%2)").arg(cardTypeList[i]).arg(cardsByType.count(cardTypeList[i]));
|
||||
|
||||
painter->setPen(Qt::white);
|
||||
painter->drawText(textRect, Qt::AlignHCenter | Qt::AlignVCenter, displayString);
|
||||
}
|
||||
}
|
||||
|
||||
void DeckViewCardContainer::addCard(DeckViewCard *card)
|
||||
{
|
||||
cards.append(card);
|
||||
cardsByType.insert(card->getInfo() ? card->getInfo()->getMainCardType() : "", card);
|
||||
}
|
||||
|
||||
void DeckViewCardContainer::removeCard(DeckViewCard *card)
|
||||
{
|
||||
cards.removeOne(card);
|
||||
cardsByType.remove(card->getInfo() ? card->getInfo()->getMainCardType() : "", card);
|
||||
}
|
||||
|
||||
QList<QPair<int, int>> DeckViewCardContainer::getRowsAndCols() const
|
||||
{
|
||||
QList<QPair<int, int>> result;
|
||||
QList<QString> cardTypeList = cardsByType.uniqueKeys();
|
||||
for (int i = 0; i < cardTypeList.size(); ++i)
|
||||
result.append(QPair<int, int>(1, cardsByType.count(cardTypeList[i])));
|
||||
return result;
|
||||
}
|
||||
|
||||
int DeckViewCardContainer::getCardTypeTextWidth() const
|
||||
{
|
||||
QFont f("Serif");
|
||||
f.setStyleHint(QFont::Serif);
|
||||
f.setPixelSize(16);
|
||||
f.setWeight(QFont::Bold);
|
||||
QFontMetrics fm(f);
|
||||
|
||||
int maxCardTypeWidth = 0;
|
||||
for (const auto &key : cardsByType.keys()) {
|
||||
int cardTypeWidth = fm.size(Qt::TextSingleLine, key).width();
|
||||
maxCardTypeWidth = qMax(maxCardTypeWidth, cardTypeWidth);
|
||||
}
|
||||
|
||||
return maxCardTypeWidth + 15;
|
||||
}
|
||||
|
||||
QSizeF DeckViewCardContainer::calculateBoundingRect(const QList<QPair<int, int>> &rowsAndCols) const
|
||||
{
|
||||
qreal totalHeight = 0;
|
||||
qreal totalWidth = 0;
|
||||
|
||||
// Calculate space needed for cards
|
||||
for (int i = 0; i < rowsAndCols.size(); ++i) {
|
||||
totalHeight += CARD_HEIGHT * rowsAndCols[i].first + paddingY;
|
||||
if (CARD_WIDTH * rowsAndCols[i].second > totalWidth)
|
||||
totalWidth = CARD_WIDTH * rowsAndCols[i].second;
|
||||
}
|
||||
|
||||
return QSizeF(getCardTypeTextWidth() + totalWidth, totalHeight + separatorY + paddingY);
|
||||
}
|
||||
|
||||
bool DeckViewCardContainer::sortCardsByName(DeckViewCard *c1, DeckViewCard *c2)
|
||||
{
|
||||
if (c1 && c2)
|
||||
return c1->getName() < c2->getName();
|
||||
return false;
|
||||
}
|
||||
|
||||
void DeckViewCardContainer::rearrangeItems(const QList<QPair<int, int>> &rowsAndCols)
|
||||
{
|
||||
currentRowsAndCols = rowsAndCols;
|
||||
|
||||
qreal yUntilNow = separatorY + paddingY;
|
||||
qreal x = (qreal)getCardTypeTextWidth();
|
||||
for (int i = 0; i < rowsAndCols.size(); ++i) {
|
||||
const int tempRows = rowsAndCols[i].first;
|
||||
const int tempCols = rowsAndCols[i].second;
|
||||
QList<QString> cardTypeList = cardsByType.uniqueKeys();
|
||||
QList<DeckViewCard *> row = cardsByType.values(cardTypeList[i]);
|
||||
std::sort(row.begin(), row.end(), DeckViewCardContainer::sortCardsByName);
|
||||
for (int j = 0; j < row.size(); ++j) {
|
||||
DeckViewCard *card = row[j];
|
||||
card->setPos(x + (j % tempCols) * CARD_WIDTH, yUntilNow + (j / tempCols) * CARD_HEIGHT);
|
||||
}
|
||||
yUntilNow += tempRows * CARD_HEIGHT + paddingY;
|
||||
}
|
||||
|
||||
prepareGeometryChange();
|
||||
QSizeF bRect = calculateBoundingRect(rowsAndCols);
|
||||
width = bRect.width();
|
||||
height = bRect.height();
|
||||
}
|
||||
|
||||
void DeckViewCardContainer::setWidth(qreal _width)
|
||||
{
|
||||
prepareGeometryChange();
|
||||
width = _width;
|
||||
update();
|
||||
}
|
||||
|
||||
DeckViewScene::DeckViewScene(QObject *parent) : QGraphicsScene(parent), locked(true), deck(0), optimalAspectRatio(1.0)
|
||||
{
|
||||
}
|
||||
|
||||
DeckViewScene::~DeckViewScene()
|
||||
{
|
||||
clearContents();
|
||||
delete deck;
|
||||
}
|
||||
|
||||
void DeckViewScene::clearContents()
|
||||
{
|
||||
QMapIterator<QString, DeckViewCardContainer *> i(cardContainers);
|
||||
while (i.hasNext())
|
||||
delete i.next().value();
|
||||
cardContainers.clear();
|
||||
}
|
||||
|
||||
void DeckViewScene::setDeck(const DeckList &_deck)
|
||||
{
|
||||
if (deck)
|
||||
delete deck;
|
||||
|
||||
deck = new DeckList(_deck);
|
||||
rebuildTree();
|
||||
applySideboardPlan(deck->getCurrentSideboardPlan());
|
||||
rearrangeItems();
|
||||
}
|
||||
|
||||
void DeckViewScene::rebuildTree()
|
||||
{
|
||||
clearContents();
|
||||
|
||||
if (!deck)
|
||||
return;
|
||||
|
||||
InnerDecklistNode *listRoot = deck->getRoot();
|
||||
for (int i = 0; i < listRoot->size(); i++) {
|
||||
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
||||
|
||||
DeckViewCardContainer *container = cardContainers.value(currentZone->getName(), 0);
|
||||
if (!container) {
|
||||
container = new DeckViewCardContainer(currentZone->getName());
|
||||
cardContainers.insert(currentZone->getName(), container);
|
||||
addItem(container);
|
||||
}
|
||||
|
||||
for (int j = 0; j < currentZone->size(); j++) {
|
||||
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
||||
if (!currentCard)
|
||||
continue;
|
||||
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
DeckViewCard *newCard = new DeckViewCard(currentCard->getName(), currentZone->getName(), container);
|
||||
container->addCard(newCard);
|
||||
emit newCardAdded(newCard);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeckViewScene::applySideboardPlan(const QList<MoveCard_ToZone> &plan)
|
||||
{
|
||||
for (int i = 0; i < plan.size(); ++i) {
|
||||
const MoveCard_ToZone &m = plan[i];
|
||||
DeckViewCardContainer *start = cardContainers.value(QString::fromStdString(m.start_zone()));
|
||||
DeckViewCardContainer *target = cardContainers.value(QString::fromStdString(m.target_zone()));
|
||||
if (!start || !target)
|
||||
continue;
|
||||
|
||||
DeckViewCard *card = 0;
|
||||
const QList<DeckViewCard *> &cardList = start->getCards();
|
||||
for (int j = 0; j < cardList.size(); ++j)
|
||||
if (cardList[j]->getName() == QString::fromStdString(m.card_name())) {
|
||||
card = cardList[j];
|
||||
break;
|
||||
}
|
||||
if (!card)
|
||||
continue;
|
||||
|
||||
start->removeCard(card);
|
||||
target->addCard(card);
|
||||
card->setParentItem(target);
|
||||
}
|
||||
}
|
||||
|
||||
void DeckViewScene::rearrangeItems()
|
||||
{
|
||||
const int spacing = CARD_HEIGHT / 3;
|
||||
QList<DeckViewCardContainer *> contList = cardContainers.values();
|
||||
|
||||
// Initialize space requirements
|
||||
QList<QList<QPair<int, int>>> rowsAndColsList;
|
||||
QList<QList<int>> cardCountList;
|
||||
for (int i = 0; i < contList.size(); ++i) {
|
||||
QList<QPair<int, int>> rowsAndCols = contList[i]->getRowsAndCols();
|
||||
rowsAndColsList.append(rowsAndCols);
|
||||
|
||||
cardCountList.append(QList<int>());
|
||||
for (int j = 0; j < rowsAndCols.size(); ++j)
|
||||
cardCountList[i].append(rowsAndCols[j].second);
|
||||
}
|
||||
|
||||
qreal totalHeight, totalWidth;
|
||||
for (;;) {
|
||||
// Calculate total size before this iteration
|
||||
totalHeight = -spacing;
|
||||
totalWidth = 0;
|
||||
for (int i = 0; i < contList.size(); ++i) {
|
||||
QSizeF contSize = contList[i]->calculateBoundingRect(rowsAndColsList[i]);
|
||||
totalHeight += contSize.height() + spacing;
|
||||
if (contSize.width() > totalWidth)
|
||||
totalWidth = contSize.width();
|
||||
}
|
||||
|
||||
// We're done when the aspect ratio shifts from too high to too low.
|
||||
if (totalWidth / totalHeight <= optimalAspectRatio)
|
||||
break;
|
||||
|
||||
// Find category with highest column count
|
||||
int maxIndex1 = -1, maxIndex2 = -1, maxCols = 0;
|
||||
for (int i = 0; i < rowsAndColsList.size(); ++i)
|
||||
for (int j = 0; j < rowsAndColsList[i].size(); ++j)
|
||||
if (rowsAndColsList[i][j].second > maxCols) {
|
||||
maxIndex1 = i;
|
||||
maxIndex2 = j;
|
||||
maxCols = rowsAndColsList[i][j].second;
|
||||
}
|
||||
|
||||
// Add row to category
|
||||
const int maxRows = rowsAndColsList[maxIndex1][maxIndex2].first;
|
||||
const int maxCardCount = cardCountList[maxIndex1][maxIndex2];
|
||||
rowsAndColsList[maxIndex1][maxIndex2] =
|
||||
QPair<int, int>(maxRows + 1, (int)qCeil((qreal)maxCardCount / (qreal)(maxRows + 1)));
|
||||
}
|
||||
|
||||
totalHeight = -spacing;
|
||||
for (int i = 0; i < contList.size(); ++i) {
|
||||
DeckViewCardContainer *c = contList[i];
|
||||
c->rearrangeItems(rowsAndColsList[i]);
|
||||
c->setPos(0, totalHeight + spacing);
|
||||
totalHeight += c->boundingRect().height() + spacing;
|
||||
}
|
||||
|
||||
totalWidth = totalHeight * optimalAspectRatio;
|
||||
for (int i = 0; i < contList.size(); ++i)
|
||||
contList[i]->setWidth(totalWidth);
|
||||
|
||||
setSceneRect(QRectF(0, 0, totalWidth, totalHeight));
|
||||
}
|
||||
|
||||
void DeckViewScene::updateContents()
|
||||
{
|
||||
rearrangeItems();
|
||||
emit sideboardPlanChanged();
|
||||
}
|
||||
|
||||
QList<MoveCard_ToZone> DeckViewScene::getSideboardPlan() const
|
||||
{
|
||||
QList<MoveCard_ToZone> result;
|
||||
QMapIterator<QString, DeckViewCardContainer *> containerIterator(cardContainers);
|
||||
while (containerIterator.hasNext()) {
|
||||
DeckViewCardContainer *cont = containerIterator.next().value();
|
||||
const QList<DeckViewCard *> cardList = cont->getCards();
|
||||
for (int i = 0; i < cardList.size(); ++i)
|
||||
if (cardList[i]->getOriginZone() != cont->getName()) {
|
||||
MoveCard_ToZone m;
|
||||
m.set_card_name(cardList[i]->getName().toStdString());
|
||||
m.set_start_zone(cardList[i]->getOriginZone().toStdString());
|
||||
m.set_target_zone(cont->getName().toStdString());
|
||||
result.append(m);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void DeckViewScene::resetSideboardPlan()
|
||||
{
|
||||
rebuildTree();
|
||||
rearrangeItems();
|
||||
}
|
||||
|
||||
DeckView::DeckView(QWidget *parent) : QGraphicsView(parent)
|
||||
{
|
||||
deckViewScene = new DeckViewScene(this);
|
||||
|
||||
setBackgroundBrush(QBrush(QColor(0, 0, 0)));
|
||||
setDragMode(RubberBandDrag);
|
||||
setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing /* | QPainter::SmoothPixmapTransform*/);
|
||||
setScene(deckViewScene);
|
||||
|
||||
connect(deckViewScene, SIGNAL(sceneRectChanged(const QRectF &)), this, SLOT(updateSceneRect(const QRectF &)));
|
||||
connect(deckViewScene, SIGNAL(newCardAdded(AbstractCardItem *)), this, SIGNAL(newCardAdded(AbstractCardItem *)));
|
||||
connect(deckViewScene, SIGNAL(sideboardPlanChanged()), this, SIGNAL(sideboardPlanChanged()));
|
||||
}
|
||||
|
||||
void DeckView::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QGraphicsView::resizeEvent(event);
|
||||
deckViewScene->setOptimalAspectRatio((qreal)width() / (qreal)height());
|
||||
deckViewScene->rearrangeItems();
|
||||
}
|
||||
|
||||
void DeckView::updateSceneRect(const QRectF &rect)
|
||||
{
|
||||
fitInView(rect, Qt::KeepAspectRatio);
|
||||
}
|
||||
|
||||
void DeckView::setDeck(const DeckList &_deck)
|
||||
{
|
||||
deckViewScene->setDeck(_deck);
|
||||
}
|
||||
|
||||
void DeckView::resetSideboardPlan()
|
||||
{
|
||||
deckViewScene->resetSideboardPlan();
|
||||
}
|
||||
166
cockatrice/src/deck/deck_view.h
Normal file
166
cockatrice/src/deck/deck_view.h
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
#ifndef DECKVIEW_H
|
||||
#define DECKVIEW_H
|
||||
|
||||
#include "../game/cards/abstract_card_drag_item.h"
|
||||
#include "pb/move_card_to_zone.pb.h"
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsView>
|
||||
#include <QMap>
|
||||
#include <QMultiMap>
|
||||
#include <QPixmap>
|
||||
|
||||
class DeckList;
|
||||
class InnerDecklistNode;
|
||||
class CardInfo;
|
||||
class DeckViewCardContainer;
|
||||
class DeckViewCardDragItem;
|
||||
class MoveCardToZone;
|
||||
|
||||
class DeckViewCard : public AbstractCardItem
|
||||
{
|
||||
private:
|
||||
QString originZone;
|
||||
DeckViewCardDragItem *dragItem;
|
||||
|
||||
public:
|
||||
DeckViewCard(const QString &_name = QString(),
|
||||
const QString &_originZone = QString(),
|
||||
QGraphicsItem *parent = nullptr);
|
||||
~DeckViewCard();
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
|
||||
const QString &getOriginZone() const
|
||||
{
|
||||
return originZone;
|
||||
}
|
||||
|
||||
protected:
|
||||
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
|
||||
void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
|
||||
};
|
||||
|
||||
class DeckViewCardDragItem : public AbstractCardDragItem
|
||||
{
|
||||
private:
|
||||
DeckViewCardContainer *currentZone;
|
||||
void handleDrop(DeckViewCardContainer *target);
|
||||
|
||||
public:
|
||||
DeckViewCardDragItem(DeckViewCard *_item, const QPointF &_hotSpot, AbstractCardDragItem *parentDrag = 0);
|
||||
void updatePosition(const QPointF &cursorScenePos);
|
||||
|
||||
protected:
|
||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
|
||||
};
|
||||
|
||||
class DeckViewCardContainer : public QGraphicsItem
|
||||
{
|
||||
private:
|
||||
static const int separatorY = 20;
|
||||
static const int paddingY = 10;
|
||||
static bool sortCardsByName(DeckViewCard *c1, DeckViewCard *c2);
|
||||
|
||||
QString name;
|
||||
QList<DeckViewCard *> cards;
|
||||
QMultiMap<QString, DeckViewCard *> cardsByType;
|
||||
QList<QPair<int, int>> currentRowsAndCols;
|
||||
qreal width, height;
|
||||
int getCardTypeTextWidth() const;
|
||||
|
||||
public:
|
||||
enum
|
||||
{
|
||||
Type = typeDeckViewCardContainer
|
||||
};
|
||||
int type() const
|
||||
{
|
||||
return Type;
|
||||
}
|
||||
DeckViewCardContainer(const QString &_name);
|
||||
QRectF boundingRect() const;
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
|
||||
void addCard(DeckViewCard *card);
|
||||
void removeCard(DeckViewCard *card);
|
||||
const QList<DeckViewCard *> &getCards() const
|
||||
{
|
||||
return cards;
|
||||
}
|
||||
const QString &getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
void setWidth(qreal _width);
|
||||
|
||||
QList<QPair<int, int>> getRowsAndCols() const;
|
||||
QSizeF calculateBoundingRect(const QList<QPair<int, int>> &rowsAndCols) const;
|
||||
void rearrangeItems(const QList<QPair<int, int>> &rowsAndCols);
|
||||
};
|
||||
|
||||
class DeckViewScene : public QGraphicsScene
|
||||
{
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void newCardAdded(AbstractCardItem *card);
|
||||
void sideboardPlanChanged();
|
||||
|
||||
private:
|
||||
bool locked;
|
||||
DeckList *deck;
|
||||
QMap<QString, DeckViewCardContainer *> cardContainers;
|
||||
qreal optimalAspectRatio;
|
||||
void clearContents();
|
||||
void rebuildTree();
|
||||
|
||||
public:
|
||||
DeckViewScene(QObject *parent = nullptr);
|
||||
~DeckViewScene();
|
||||
void setLocked(bool _locked)
|
||||
{
|
||||
locked = _locked;
|
||||
}
|
||||
bool getLocked() const
|
||||
{
|
||||
return locked;
|
||||
}
|
||||
void setDeck(const DeckList &_deck);
|
||||
void setOptimalAspectRatio(qreal _optimalAspectRatio)
|
||||
{
|
||||
optimalAspectRatio = _optimalAspectRatio;
|
||||
}
|
||||
void rearrangeItems();
|
||||
void updateContents();
|
||||
QList<MoveCard_ToZone> getSideboardPlan() const;
|
||||
void resetSideboardPlan();
|
||||
void applySideboardPlan(const QList<MoveCard_ToZone> &plan);
|
||||
};
|
||||
|
||||
class DeckView : public QGraphicsView
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DeckViewScene *deckViewScene;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
public slots:
|
||||
void updateSceneRect(const QRectF &rect);
|
||||
signals:
|
||||
void newCardAdded(AbstractCardItem *card);
|
||||
void sideboardPlanChanged();
|
||||
|
||||
public:
|
||||
DeckView(QWidget *parent = nullptr);
|
||||
void setDeck(const DeckList &_deck);
|
||||
void setLocked(bool _locked)
|
||||
{
|
||||
deckViewScene->setLocked(_locked);
|
||||
}
|
||||
QList<MoveCard_ToZone> getSideboardPlan() const
|
||||
{
|
||||
return deckViewScene->getSideboardPlan();
|
||||
}
|
||||
void mouseDoubleClickEvent(QMouseEvent *event);
|
||||
void resetSideboardPlan();
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue