servatrice/cockatrice/src/decklist.cpp
Max-Wilhelm Bruker f33d790822 small changes
2009-08-01 19:10:10 +02:00

427 lines
11 KiB
C++

#include <QFile>
#include <QFileDialog>
#include <QTextStream>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QProgressDialog>
#include <QVariant>
#include <QSettings>
#include "decklist.h"
#include "carddatabase.h"
AbstractDecklistNode::AbstractDecklistNode(InnerDecklistNode *_parent)
: parent(_parent)
{
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));
}
};
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(CardDatabase *_db, QObject *parent)
: QObject(parent), db(_db)
{
root = new InnerDecklistNode;
}
DeckList::~DeckList()
{
delete root;
}
QXmlStreamReader &operator>>(QXmlStreamReader &xml, InnerDecklistNode *node)
{
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement)
break;
if (xml.name() == "zone")
xml >> new InnerDecklistNode(xml.attributes().value("name").toString(), node);
else if (xml.name() == "card") {
const int number = xml.attributes().value("number").toString().toInt();
const QString card = xml.attributes().value("name").toString();
new DecklistCardNode(card, number, node);
while (!xml.atEnd())
if (xml.readNext() == QXmlStreamReader::EndElement)
break;
}
}
return xml;
}
bool DeckList::loadFromFile_Native(QIODevice *device)
{
QXmlStreamReader xml(device);
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::StartElement) {
if (xml.name() != "cockatrice_deck")
return false;
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement)
break;
if (xml.name() == "deckname")
name = xml.readElementText();
else if (xml.name() == "comments")
comments = xml.readElementText();
else if (xml.name() == "zone")
xml >> new InnerDecklistNode(xml.attributes().value("name").toString(), root);
}
}
}
return true;
}
QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const AbstractDecklistNode *node)
{
const InnerDecklistNode *inner = dynamic_cast<const InnerDecklistNode *>(node);
if (inner) {
xml.writeStartElement("zone");
xml.writeAttribute("name", inner->getName());
for (int i = 0; i < inner->size(); i++)
xml << inner->at(i);
xml.writeEndElement(); // zone
} else {
const AbstractDecklistCardNode *card = dynamic_cast<const AbstractDecklistCardNode *>(node);
xml.writeEmptyElement("card");
xml.writeAttribute("number", QString::number(card->getNumber()));
xml.writeAttribute("name", card->getName());
}
return xml;
}
bool DeckList::saveToFile_Native(QIODevice *device)
{
QXmlStreamWriter xml(device);
xml.setAutoFormatting(true);
xml.writeStartDocument();
xml.writeStartElement("cockatrice_deck");
xml.writeAttribute("version", "1");
xml.writeTextElement("deckname", name);
xml.writeTextElement("comments", comments);
for (int i = 0; i < root->size(); i++)
xml << root->at(i);
xml.writeEndElement(); // cockatrice_deck
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, QWidget *parent)
{
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();
cacheCardPictures(parent);
}
return result;
}
const QStringList DeckList::fileNameFilters = QStringList()
<< QObject::tr("Cockatrice decks (*.cod)")
<< QObject::tr("Plain text decks (*.dec *.mwDeck)")
<< QObject::tr("All files (*.*)");
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;
}
bool DeckList::loadDialog(QWidget *parent)
{
QFileDialog dialog(parent, tr("Load deck"));
QSettings settings;
dialog.setDirectory(settings.value("paths/decks").toString());
dialog.setNameFilters(fileNameFilters);
if (!dialog.exec())
return false;
QString fileName = dialog.selectedFiles().at(0);
FileFormat fmt;
switch (fileNameFilters.indexOf(dialog.selectedNameFilter())) {
case 0: fmt = CockatriceFormat; break;
case 1: fmt = PlainTextFormat; break;
default: fmt = PlainTextFormat; break;
}
if (loadFromFile(fileName, fmt, parent)) {
lastFileName = fileName;
lastFileFormat = fmt;
return true;
}
return false;
}
bool DeckList::saveDialog(QWidget *parent)
{
QFileDialog dialog(parent, tr("Save deck"));
QSettings settings;
dialog.setDirectory(settings.value("paths/decks").toString());
dialog.setAcceptMode(QFileDialog::AcceptSave);
dialog.setConfirmOverwrite(true);
dialog.setDefaultSuffix("cod");
dialog.setNameFilters(fileNameFilters);
if (!dialog.exec())
return false;
QString fileName = dialog.selectedFiles().at(0);
DeckList::FileFormat fmt;
switch (fileNameFilters.indexOf(dialog.selectedNameFilter())) {
case 0: fmt = DeckList::CockatriceFormat; break;
case 1: fmt = DeckList::PlainTextFormat; break;
default: fmt = DeckList::PlainTextFormat; break;
}
if (saveToFile(fileName, fmt)) {
lastFileName = fileName;
lastFileFormat = fmt;
return true;
}
return false;
}
void DeckList::cacheCardPicturesHelper(InnerDecklistNode *item, QProgressDialog *progress)
{
for (int i = 0; i < item->size(); i++) {
DecklistCardNode *node = dynamic_cast<DecklistCardNode *>(item->at(i));
if (node) {
db->getCard(node->getName())->loadPixmap();
progress->setValue(progress->value() + 1);
} else
cacheCardPicturesHelper(dynamic_cast<InnerDecklistNode *>(item->at(i)), progress);
}
}
void DeckList::cacheCardPictures(QWidget *parent)
{
int totalCards = root->recursiveCount();
QProgressDialog progress(tr("Caching card pictures..."), QString(), 0, totalCards, parent);
progress.setMinimumDuration(1000);
progress.setWindowModality(Qt::WindowModal);
cacheCardPicturesHelper(root, &progress);
}
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;
}