mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-10 16:24:45 -07:00
decklist moved to common
This commit is contained in:
parent
c1b7522840
commit
c5bf72b1bf
3 changed files with 109 additions and 59 deletions
374
common/decklist.cpp
Normal file
374
common/decklist.cpp
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QTextStream>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
#include <QVariant>
|
||||
#include "decklist.h"
|
||||
#include "carddatabase.h"
|
||||
|
||||
AbstractDecklistNode::AbstractDecklistNode(InnerDecklistNode *_parent)
|
||||
: parent(_parent), currentItem(0)
|
||||
{
|
||||
if (parent)
|
||||
parent->append(this);
|
||||
}
|
||||
|
||||
int AbstractDecklistNode::depth() const
|
||||
{
|
||||
if (parent)
|
||||
return parent->depth() + 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
InnerDecklistNode::~InnerDecklistNode()
|
||||
{
|
||||
clearTree();
|
||||
}
|
||||
|
||||
QString InnerDecklistNode::getVisibleName() const
|
||||
{
|
||||
if (name == "main")
|
||||
return QObject::tr("Maindeck");
|
||||
else if (name == "side")
|
||||
return QObject::tr("Sideboard");
|
||||
else
|
||||
return getName();
|
||||
}
|
||||
|
||||
void InnerDecklistNode::clearTree()
|
||||
{
|
||||
for (int i = 0; i < size(); i++)
|
||||
delete at(i);
|
||||
clear();
|
||||
}
|
||||
|
||||
AbstractDecklistNode *InnerDecklistNode::findChild(const QString &name)
|
||||
{
|
||||
for (int i = 0; i < size(); i++)
|
||||
if (at(i)->getName() == name)
|
||||
return at(i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int InnerDecklistNode::height() const
|
||||
{
|
||||
Q_ASSERT(!isEmpty());
|
||||
return at(0)->height() + 1;
|
||||
}
|
||||
|
||||
int InnerDecklistNode::recursiveCount(bool countTotalCards) const
|
||||
{
|
||||
int result = 0;
|
||||
for (int i = 0; i < size(); i++) {
|
||||
InnerDecklistNode *node = dynamic_cast<InnerDecklistNode *>(at(i));
|
||||
if (node)
|
||||
result += node->recursiveCount(countTotalCards);
|
||||
else if (countTotalCards)
|
||||
result += dynamic_cast<AbstractDecklistCardNode *>(at(i))->getNumber();
|
||||
else
|
||||
result += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InnerDecklistNode::compare(AbstractDecklistNode *other) const
|
||||
{
|
||||
InnerDecklistNode *other2 = dynamic_cast<InnerDecklistNode *>(other);
|
||||
if (other2)
|
||||
return (getName() > other->getName());
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AbstractDecklistCardNode::compare(AbstractDecklistNode *other) const
|
||||
{
|
||||
AbstractDecklistCardNode *other2 = dynamic_cast<AbstractDecklistCardNode *>(other);
|
||||
if (other2)
|
||||
return (getName() > other->getName());
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
class InnerDecklistNode::compareFunctor {
|
||||
private:
|
||||
Qt::SortOrder order;
|
||||
public:
|
||||
compareFunctor(Qt::SortOrder _order) : order(_order) { }
|
||||
inline bool operator()(QPair<int, AbstractDecklistNode *> a, QPair<int, AbstractDecklistNode *> b) const
|
||||
{
|
||||
return (order == Qt::AscendingOrder) ^ (a.second->compare(b.second));
|
||||
}
|
||||
};
|
||||
|
||||
bool InnerDecklistNode::readElement(QXmlStreamReader *xml)
|
||||
{
|
||||
if (currentItem) {
|
||||
if (currentItem->readElement(xml))
|
||||
currentItem = 0;
|
||||
return false;
|
||||
}
|
||||
if (xml->isStartElement() && (xml->name() == "zone"))
|
||||
currentItem = new InnerDecklistNode(xml->attributes().value("name").toString(), this);
|
||||
else if (xml->isStartElement() && (xml->name() == "card"))
|
||||
currentItem = new DecklistCardNode(xml->attributes().value("name").toString(), xml->attributes().value("number").toString().toInt(), this);
|
||||
else if (xml->isEndElement() && (xml->name() == "zone"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InnerDecklistNode::writeElement(QXmlStreamWriter *xml)
|
||||
{
|
||||
xml->writeStartElement("zone");
|
||||
xml->writeAttribute("name", name);
|
||||
for (int i = 0; i < size(); i++)
|
||||
at(i)->writeElement(xml);
|
||||
xml->writeEndElement(); // zone
|
||||
}
|
||||
|
||||
bool AbstractDecklistCardNode::readElement(QXmlStreamReader *xml)
|
||||
{
|
||||
if (xml->isEndElement())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void AbstractDecklistCardNode::writeElement(QXmlStreamWriter *xml)
|
||||
{
|
||||
xml->writeEmptyElement("card");
|
||||
xml->writeAttribute("number", QString::number(getNumber()));
|
||||
xml->writeAttribute("name", getName());
|
||||
}
|
||||
|
||||
QVector<QPair<int, int> > InnerDecklistNode::sort(Qt::SortOrder order)
|
||||
{
|
||||
QVector<QPair<int, int> > result(size());
|
||||
|
||||
// Initialize temporary list with contents of current list
|
||||
QVector<QPair<int, AbstractDecklistNode *> > tempList(size());
|
||||
for (int i = size() - 1; i >= 0; --i) {
|
||||
tempList[i].first = i;
|
||||
tempList[i].second = at(i);
|
||||
}
|
||||
|
||||
// Sort temporary list
|
||||
compareFunctor cmp(order);
|
||||
qSort(tempList.begin(), tempList.end(), cmp);
|
||||
|
||||
// Map old indexes to new indexes and
|
||||
// copy temporary list to the current one
|
||||
for (int i = size() - 1; i >= 0; --i) {
|
||||
result[i].first = tempList[i].first;
|
||||
result[i].second = i;
|
||||
replace(i, tempList[i].second);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DeckList::DeckList(QObject *parent)
|
||||
: QObject(parent), currentZone(0)
|
||||
{
|
||||
root = new InnerDecklistNode;
|
||||
}
|
||||
|
||||
DeckList::~DeckList()
|
||||
{
|
||||
delete root;
|
||||
}
|
||||
|
||||
bool DeckList::readElement(QXmlStreamReader *xml)
|
||||
{
|
||||
if (currentZone) {
|
||||
if (currentZone->readElement(xml))
|
||||
currentZone = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (xml->isEndElement()) {
|
||||
if (xml->name() == "deckname")
|
||||
name = currentElementText;
|
||||
else if (xml->name() == "comments")
|
||||
comments = currentElementText;
|
||||
else
|
||||
return false;
|
||||
currentElementText.clear();
|
||||
} else if (xml->isStartElement() && (xml->name() == "zone"))
|
||||
currentZone = new InnerDecklistNode(xml->attributes().value("name").toString(), root);
|
||||
else if (xml->isCharacters() && !xml->isWhitespace())
|
||||
currentElementText = xml->text().toString();
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeckList::writeElement(QXmlStreamWriter *xml)
|
||||
{
|
||||
xml->writeStartElement("cockatrice_deck");
|
||||
xml->writeAttribute("version", "1");
|
||||
xml->writeTextElement("deckname", name);
|
||||
xml->writeTextElement("comments", comments);
|
||||
|
||||
for (int i = 0; i < root->size(); i++)
|
||||
root->at(i)->writeElement(xml);
|
||||
|
||||
xml->writeEndElement(); // cockatrice_deck
|
||||
}
|
||||
|
||||
bool DeckList::loadFromFile_Native(QIODevice *device)
|
||||
{
|
||||
QXmlStreamReader xml(device);
|
||||
while (!xml.atEnd()) {
|
||||
xml.readNext();
|
||||
if (xml.isStartElement()) {
|
||||
if (xml.name() != "cockatrice_deck")
|
||||
return false;
|
||||
while (!xml.atEnd()) {
|
||||
xml.readNext();
|
||||
if (!readElement(&xml))
|
||||
if (xml.isEndElement() && (xml.name() == "cockatrice_deck"))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeckList::saveToFile_Native(QIODevice *device)
|
||||
{
|
||||
QXmlStreamWriter xml(device);
|
||||
xml.setAutoFormatting(true);
|
||||
xml.writeStartDocument();
|
||||
|
||||
writeElement(&xml);
|
||||
|
||||
xml.writeEndDocument();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeckList::loadFromFile_Plain(QIODevice *device)
|
||||
{
|
||||
InnerDecklistNode *main = 0, *side = 0;
|
||||
|
||||
QTextStream in(device);
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine().simplified();
|
||||
if (line.startsWith("//"))
|
||||
continue;
|
||||
|
||||
InnerDecklistNode *zone;
|
||||
if (line.startsWith("SB:", Qt::CaseInsensitive)) {
|
||||
line = line.mid(3).trimmed();
|
||||
if (!side)
|
||||
side = new InnerDecklistNode("side", root);
|
||||
zone = side;
|
||||
} else {
|
||||
if (!main)
|
||||
main = new InnerDecklistNode("main", root);
|
||||
zone = main;
|
||||
}
|
||||
|
||||
// Filter out MWS edition symbols and basic land extras
|
||||
QRegExp rx("\\[.*\\]");
|
||||
line.remove(rx);
|
||||
rx.setPattern("\\(.*\\)");
|
||||
line.remove(rx);
|
||||
line = line.simplified();
|
||||
|
||||
int i = line.indexOf(' ');
|
||||
bool ok;
|
||||
int number = line.left(i).toInt(&ok);
|
||||
if (!ok)
|
||||
continue;
|
||||
new DecklistCardNode(line.mid(i + 1), number, zone);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeckList::saveToFile_Plain(QIODevice *device)
|
||||
{
|
||||
// Support for this is only possible if the internal structure doesn't get more complicated.
|
||||
QTextStream out(device);
|
||||
for (int i = 0; i < root->size(); i++) {
|
||||
InnerDecklistNode *node = dynamic_cast<InnerDecklistNode *>(root->at(i));
|
||||
for (int j = 0; j < node->size(); j++) {
|
||||
DecklistCardNode *card = dynamic_cast<DecklistCardNode *>(node->at(j));
|
||||
out << QString("%1%2 %3\n").arg(node->getName() == "side" ? "SB: " : "").arg(card->getNumber()).arg(card->getName());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeckList::loadFromFile(const QString &fileName, FileFormat fmt)
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return false;
|
||||
cleanList();
|
||||
|
||||
bool result = false;
|
||||
switch (fmt) {
|
||||
case PlainTextFormat: result = loadFromFile_Plain(&file); break;
|
||||
case CockatriceFormat: result = loadFromFile_Native(&file); break;
|
||||
}
|
||||
if (result)
|
||||
emit deckLoaded();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DeckList::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;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void DeckList::cleanList()
|
||||
{
|
||||
root->clearTree();
|
||||
setName();
|
||||
setComments();
|
||||
}
|
||||
|
||||
DecklistCardNode *DeckList::addCard(const QString &cardName, const QString &zoneName)
|
||||
{
|
||||
InnerDecklistNode *zoneNode = dynamic_cast<InnerDecklistNode *>(root->findChild(zoneName));
|
||||
if (!zoneNode)
|
||||
zoneNode = new InnerDecklistNode(zoneName, root);
|
||||
|
||||
return new DecklistCardNode(cardName, 1, zoneNode);
|
||||
}
|
||||
|
||||
bool DeckList::deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode)
|
||||
{
|
||||
if (node == root)
|
||||
return true;
|
||||
if (!rootNode)
|
||||
rootNode = root;
|
||||
|
||||
int index = rootNode->indexOf(node);
|
||||
if (index != -1) {
|
||||
delete rootNode->takeAt(index);
|
||||
if (!rootNode->size())
|
||||
deleteNode(rootNode, rootNode->getParent());
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < rootNode->size(); i++) {
|
||||
InnerDecklistNode *inner = dynamic_cast<InnerDecklistNode *>(rootNode->at(i));
|
||||
if (inner)
|
||||
if (deleteNode(node, inner))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
123
common/decklist.h
Normal file
123
common/decklist.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
#ifndef DECKLIST_H
|
||||
#define DECKLIST_H
|
||||
|
||||
#include <QList>
|
||||
#include <QVector>
|
||||
#include <QPair>
|
||||
#include <QObject>
|
||||
|
||||
class CardDatabase;
|
||||
class QIODevice;
|
||||
class QXmlStreamReader;
|
||||
class QXmlStreamWriter;
|
||||
|
||||
class InnerDecklistNode;
|
||||
|
||||
class AbstractDecklistNode {
|
||||
protected:
|
||||
InnerDecklistNode *parent;
|
||||
AbstractDecklistNode *currentItem;
|
||||
public:
|
||||
AbstractDecklistNode(InnerDecklistNode *_parent = 0);
|
||||
virtual ~AbstractDecklistNode() { }
|
||||
virtual QString getName() const = 0;
|
||||
InnerDecklistNode *getParent() const { return parent; }
|
||||
int depth() const;
|
||||
virtual int height() const = 0;
|
||||
virtual bool compare(AbstractDecklistNode *other) const = 0;
|
||||
|
||||
virtual bool readElement(QXmlStreamReader *xml) = 0;
|
||||
virtual void writeElement(QXmlStreamWriter *xml) = 0;
|
||||
};
|
||||
|
||||
class InnerDecklistNode : public AbstractDecklistNode, public QList<AbstractDecklistNode *> {
|
||||
private:
|
||||
QString name;
|
||||
class compareFunctor;
|
||||
public:
|
||||
InnerDecklistNode(const QString &_name = QString(), InnerDecklistNode *_parent = 0) : AbstractDecklistNode(_parent), name(_name) { }
|
||||
virtual ~InnerDecklistNode();
|
||||
QString getName() const { return name; }
|
||||
void setName(const QString &_name) { name = _name; }
|
||||
virtual QString getVisibleName() const;
|
||||
void clearTree();
|
||||
AbstractDecklistNode *findChild(const QString &name);
|
||||
int height() const;
|
||||
int recursiveCount(bool countTotalCards = false) const;
|
||||
bool compare(AbstractDecklistNode *other) const;
|
||||
QVector<QPair<int, int> > sort(Qt::SortOrder order = Qt::AscendingOrder);
|
||||
|
||||
bool readElement(QXmlStreamReader *xml);
|
||||
void writeElement(QXmlStreamWriter *xml);
|
||||
};
|
||||
|
||||
class AbstractDecklistCardNode : public AbstractDecklistNode {
|
||||
public:
|
||||
AbstractDecklistCardNode(InnerDecklistNode *_parent = 0) : AbstractDecklistNode(_parent) { }
|
||||
virtual int getNumber() const = 0;
|
||||
virtual void setNumber(int _number) = 0;
|
||||
virtual QString getName() const = 0;
|
||||
virtual void setName(const QString &_name) = 0;
|
||||
int height() const { return 0; }
|
||||
bool compare(AbstractDecklistNode *other) const;
|
||||
|
||||
bool readElement(QXmlStreamReader *xml);
|
||||
void writeElement(QXmlStreamWriter *xml);
|
||||
};
|
||||
|
||||
class DecklistCardNode : public AbstractDecklistCardNode {
|
||||
private:
|
||||
QString name;
|
||||
int number;
|
||||
public:
|
||||
DecklistCardNode(const QString &_name = QString(), int _number = 1, InnerDecklistNode *_parent = 0) : AbstractDecklistCardNode(_parent), name(_name), number(_number) { }
|
||||
int getNumber() const { return number; }
|
||||
void setNumber(int _number) { number = _number; }
|
||||
QString getName() const { return name; }
|
||||
void setName(const QString &_name) { name = _name; }
|
||||
};
|
||||
|
||||
class DeckList : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum FileFormat { PlainTextFormat, CockatriceFormat };
|
||||
private:
|
||||
QString name, comments;
|
||||
QString lastFileName;
|
||||
FileFormat lastFileFormat;
|
||||
InnerDecklistNode *root;
|
||||
InnerDecklistNode *currentZone;
|
||||
QString currentElementText;
|
||||
signals:
|
||||
void deckLoaded();
|
||||
public slots:
|
||||
void setName(const QString &_name = QString()) { name = _name; }
|
||||
void setComments(const QString &_comments = QString()) { comments = _comments; }
|
||||
public:
|
||||
DeckList(QObject *parent = 0);
|
||||
~DeckList();
|
||||
QString getName() const { return name; }
|
||||
QString getComments() const { return comments; }
|
||||
QString getLastFileName() const { return lastFileName; }
|
||||
FileFormat getLastFileFormat() const { return lastFileFormat; }
|
||||
|
||||
bool readElement(QXmlStreamReader *xml);
|
||||
void writeElement(QXmlStreamWriter *xml);
|
||||
|
||||
bool loadFromFile_Native(QIODevice *device);
|
||||
bool saveToFile_Native(QIODevice *device);
|
||||
bool loadFromFile_Plain(QIODevice *device);
|
||||
bool saveToFile_Plain(QIODevice *device);
|
||||
bool loadFromFile(const QString &fileName, FileFormat fmt);
|
||||
bool saveToFile(const QString &fileName, FileFormat fmt);
|
||||
bool loadDialog(QWidget *parent = 0);
|
||||
bool saveDialog(QWidget *parent = 0);
|
||||
|
||||
void cleanList();
|
||||
|
||||
InnerDecklistNode *getRoot() const { return root; }
|
||||
DecklistCardNode *addCard(const QString &cardName, const QString &zoneName);
|
||||
bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = 0);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -156,7 +156,7 @@ bool Response_DeckList::Directory::readElement(QXmlStreamReader *xml)
|
|||
if (currentItem) {
|
||||
if (currentItem->readElement(xml))
|
||||
currentItem = 0;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (xml->isStartElement() && (xml->name() == "directory")) {
|
||||
currentItem = new Directory(xml->attributes().value("name").toString());
|
||||
|
|
@ -164,9 +164,10 @@ bool Response_DeckList::Directory::readElement(QXmlStreamReader *xml)
|
|||
} else if (xml->isStartElement() && (xml->name() == "file")) {
|
||||
currentItem = new File(xml->attributes().value("name").toString(), xml->attributes().value("id").toString().toInt());
|
||||
append(currentItem);
|
||||
} else
|
||||
return false;
|
||||
return true;
|
||||
} else if (xml->isEndElement() && (xml->name() == "directory"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Response_DeckList::Directory::writeElement(QXmlStreamWriter *xml)
|
||||
|
|
@ -196,7 +197,7 @@ bool Response_DeckList::readElement(QXmlStreamReader *xml)
|
|||
return false;
|
||||
}
|
||||
|
||||
return root->readElement(xml);
|
||||
return !root->readElement(xml);
|
||||
}
|
||||
|
||||
void Response_DeckList::writeElement(QXmlStreamWriter *xml)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue