diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index ac7ef897..29570fbd 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -116,6 +116,7 @@ SET(cockatrice_SOURCES src/logger.cpp src/releasechannel.cpp src/userconnection_information.cpp + src/spoilerbackgroundupdater.cpp ${VERSION_STRING_CPP} ) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 489b78d9..468196f8 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -1,13 +1,12 @@ #include "carddatabase.h" #include "pictureloader.h" #include "settingscache.h" +#include "spoilerbackgroundupdater.h" #include #include #include -#include #include -#include #include const int CardDatabase::versionNeeded = 3; @@ -15,6 +14,12 @@ const char* CardDatabase::TOKENS_SETNAME = "TK"; static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardSet *set) { + if (set == nullptr) + { + qDebug() << "&operator<< set is nullptr"; + return xml; + } + xml.writeStartElement("set"); xml.writeTextElement("name", set->getShortName()); xml.writeTextElement("longname", set->getLongName()); @@ -68,12 +73,19 @@ void CardSet::setIsKnown(bool _isknown) settingsCache->cardDatabase().setIsKnown(shortName,_isknown); } -class SetList::KeyCompareFunctor { -public: - inline bool operator()(CardSet *a, CardSet *b) const - { - return a->getSortKey() < b->getSortKey(); - } +class SetList::KeyCompareFunctor +{ + public: + inline bool operator()(CardSet *a, CardSet *b) const + { + if (a == nullptr || b == nullptr) + { + qDebug() << "SetList::KeyCompareFunctor a or b is null"; + return false; + } + + return a->getSortKey() < b->getSortKey(); + } }; void SetList::sortByKey() @@ -83,24 +95,28 @@ void SetList::sortByKey() int SetList::getEnabledSetsNum() { - int num=0; + int num = 0; for (int i = 0; i < size(); ++i) { CardSet *set = at(i); - if(set->getEnabled()) + if (set && set->getEnabled()) + { ++num; + } } return num; } int SetList::getUnknownSetsNum() { - int num=0; + int num = 0; for (int i = 0; i < size(); ++i) { CardSet *set = at(i); - if(!set->getIsKnown() && !set->getIsKnownIgnored()) + if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) + { ++num; + } } return num; } @@ -111,8 +127,10 @@ QStringList SetList::getUnknownSetsNames() for (int i = 0; i < size(); ++i) { CardSet *set = at(i); - if(!set->getIsKnown() && !set->getIsKnownIgnored()) + if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) + { sets << set->getShortName(); + } } return sets; } @@ -122,12 +140,12 @@ void SetList::enableAllUnknown() for (int i = 0; i < size(); ++i) { CardSet *set = at(i); - if(!set->getIsKnown() && !set->getIsKnownIgnored()) + if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) { set->setIsKnown(true); set->setEnabled(true); } - else if (set->getIsKnownIgnored() && !set->getEnabled()) + else if (set && set->getIsKnownIgnored() && !set->getEnabled()) { set->setEnabled(true); } @@ -139,8 +157,18 @@ void SetList::enableAll() for (int i = 0; i < size(); ++i) { CardSet *set = at(i); - if(!set->getIsKnownIgnored()) + + if (set == nullptr) + { + qDebug() << "enabledAll has null"; + continue; + } + + if (!set->getIsKnownIgnored()) + { set->setIsKnown(true); + } + set->setEnabled(true); } } @@ -150,12 +178,12 @@ void SetList::markAllAsKnown() for (int i = 0; i < size(); ++i) { CardSet *set = at(i); - if(!set->getIsKnown() && !set->getIsKnownIgnored()) + if(set && !set->getIsKnown() && !set->getIsKnownIgnored()) { set->setIsKnown(true); set->setEnabled(false); } - else if (set->getIsKnownIgnored() && !set->getEnabled()) + else if (set && set->getIsKnownIgnored() && !set->getEnabled()) { set->setEnabled(true); } @@ -170,11 +198,21 @@ void SetList::guessSortKeys() for (int i = 0; i < size(); ++i) { CardSet *set = at(i); + if (set == nullptr) + { + qDebug() << "guessSortKeys set is null"; + continue; + } + QDate date = set->getReleaseDate(); - if(date.isNull()) - set->setSortKey(aHundredYears); + if (date.isNull()) + { + set->setSortKey(static_cast(aHundredYears)); + } else - set->setSortKey(date.daysTo(distantFuture)); + { + set->setSortKey(static_cast(date.daysTo(distantFuture))); + } } } @@ -213,9 +251,9 @@ CardInfo::CardInfo(const QString &_name, upsideDownArt(_upsideDownArt), loyalty(_loyalty), customPicURLs(_customPicURLs), - muIds(_muIds), - collectorNumbers(_collectorNumbers), - rarities(_rarities), + muIds(std::move(_muIds)), + collectorNumbers(std::move(_collectorNumbers)), + rarities(std::move(_rarities)), cipt(_cipt), tableRow(_tableRow) { @@ -223,7 +261,9 @@ CardInfo::CardInfo(const QString &_name, simpleName = CardInfo::simplifyName(name); for (int i = 0; i < sets.size(); i++) + { sets[i]->append(this); + } refreshCachedSetNames(); } @@ -243,11 +283,20 @@ QString CardInfo::getMainCardType() const int pos; if ((pos = result.indexOf('-')) != -1) + { result.remove(pos, result.length()); + } + if ((pos = result.indexOf("—")) != -1) + { result.remove(pos, result.length()); + } + if ((pos = result.indexOf("//")) != -1) + { result.remove(pos, result.length()); + } + result = result.simplified(); /* Legendary Artifact Creature @@ -255,7 +304,9 @@ QString CardInfo::getMainCardType() const */ if ((pos = result.lastIndexOf(' ')) != -1) + { result = result.mid(pos + 1); + } /* Creature Instant @@ -273,6 +324,12 @@ QString CardInfo::getCorrectedName() const void CardInfo::addToSet(CardSet *set) { + if (set == nullptr) + { + qDebug() << "addToSet(nullptr)"; + return; + } + set->append(this); sets << set; @@ -285,14 +342,17 @@ void CardInfo::refreshCachedSetNames() QStringList setList; for (int i = 0; i < sets.size(); i++) { - if(sets[i]->getEnabled()) + if (sets[i]->getEnabled()) + { setList << sets[i]->getShortName(); + } } setsNames = setList.join(", "); } -QString CardInfo::simplifyName(const QString &name) { +QString CardInfo::simplifyName(const QString &name) +{ QString simpleName(name); // So Aetherling would work, but not Ætherling since 'Æ' would get replaced @@ -318,25 +378,29 @@ const QChar CardInfo::getColorChar() const { case 0: return QChar(); - break; case 1: return colors.first().isEmpty() ? QChar() : colors.first().at(0); - break; default: return QChar('m'); - break; } } static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) { + if (info == nullptr) + { + qDebug() << "operator<< info is nullptr"; + return xml; + } + xml.writeStartElement("card"); xml.writeTextElement("name", info->getName()); const SetList &sets = info->getSets(); QString tmpString; QString tmpSet; - for (int i = 0; i < sets.size(); i++) { + for (int i = 0; i < sets.size(); i++) + { xml.writeStartElement("set"); tmpSet=sets[i]->getShortName(); @@ -344,81 +408,121 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) xml.writeAttribute("muId", QString::number(info->getMuId(tmpSet))); tmpString = info->getCollectorNumber(tmpSet); - if(!tmpString.isEmpty()) + if (!tmpString.isEmpty()) + { xml.writeAttribute("num", info->getCollectorNumber(tmpSet)); + } tmpString = info->getCustomPicURL(tmpSet); - if(!tmpString.isEmpty()) + if (!tmpString.isEmpty()) + { xml.writeAttribute("picURL", tmpString); + } xml.writeCharacters(tmpSet); xml.writeEndElement(); } const QStringList &colors = info->getColors(); for (int i = 0; i < colors.size(); i++) + { xml.writeTextElement("color", colors[i]); + } const QList related = info->getRelatedCards(); - for (int i = 0; i < related.size(); i++) { - + for (auto i : related) + { xml.writeStartElement("related"); - if (related[i]->getDoesAttach()) + if (i->getDoesAttach()) + { xml.writeAttribute("attach", "attach"); - if (related[i]->getIsCreateAllExclusion()) + } + if (i->getIsCreateAllExclusion()) + { xml.writeAttribute("exclude", "exclude"); - if (related[i]->getIsVariable()) { - if (1 == related[i]->getDefaultCount()) + } + + if (i->getIsVariable()) + { + if (1 == i->getDefaultCount()) + { xml.writeAttribute("count", "x"); + } else - xml.writeAttribute("count", "x=" + QString::number(related[i]->getDefaultCount())); + { + xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount())); + } } - else if (1 != related[i]->getDefaultCount()) { - xml.writeAttribute("count", QString::number(related[i]->getDefaultCount())); + else if (1 != i->getDefaultCount()) + { + xml.writeAttribute("count", QString::number(i->getDefaultCount())); } - xml.writeCharacters(related[i]->getName()); + xml.writeCharacters(i->getName()); xml.writeEndElement(); } const QList reverseRelated = info->getReverseRelatedCards(); - for (int i = 0; i < reverseRelated.size(); i++) { + for (auto i : reverseRelated) + { xml.writeStartElement("reverse-related"); - if (reverseRelated[i]->getDoesAttach()) + if (i->getDoesAttach()) + { xml.writeAttribute("attach", "attach"); - if (reverseRelated[i]->getIsCreateAllExclusion()) + } + + if (i->getIsCreateAllExclusion()) + { xml.writeAttribute("exclude", "exclude"); - if (reverseRelated[i]->getIsVariable()) { - if (1 == reverseRelated[i]->getDefaultCount()) + } + + if (i->getIsVariable()) + { + if (1 == i->getDefaultCount()) + { xml.writeAttribute("count", "x"); + } else - xml.writeAttribute("count", "x=" + QString::number(reverseRelated[i]->getDefaultCount())); + { + xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount())); + } } - else if (1 != reverseRelated[i]->getDefaultCount()) { - xml.writeAttribute("count", QString::number(reverseRelated[i]->getDefaultCount())); + else if (1 != i->getDefaultCount()) + { + xml.writeAttribute("count", QString::number(i->getDefaultCount())); } - xml.writeCharacters(reverseRelated[i]->getName()); + xml.writeCharacters(i->getName()); xml.writeEndElement(); } xml.writeTextElement("manacost", info->getManaCost()); xml.writeTextElement("cmc", info->getCmc()); xml.writeTextElement("type", info->getCardType()); if (!info->getPowTough().isEmpty()) + { xml.writeTextElement("pt", info->getPowTough()); + } xml.writeTextElement("tablerow", QString::number(info->getTableRow())); xml.writeTextElement("text", info->getText()); if (info->getMainCardType() == "Planeswalker") + { xml.writeTextElement("loyalty", QString::number(info->getLoyalty())); + } if (info->getCipt()) + { xml.writeTextElement("cipt", "1"); + } if (info->getIsToken()) + { xml.writeTextElement("token", "1"); + } if (info->getUpsideDownArt()) + { xml.writeTextElement("upsidedown", "1"); + } + xml.writeEndElement(); // card return xml; } -CardDatabase::CardDatabase(QObject *parent) - : QObject(parent), loadStatus(NotLoaded) +CardDatabase::CardDatabase(QObject *parent) : QObject(parent), loadStatus(NotLoaded) { connect(settingsCache, SIGNAL(cardDatabasePathChanged()), this, SLOT(loadCardDatabases())); } @@ -430,11 +534,17 @@ CardDatabase::~CardDatabase() void CardDatabase::clear() { + clearDatabaseMutex->lock(); + QHashIterator i(cards); - while (i.hasNext()) { + while (i.hasNext()) + { i.next(); - removeCard(i.value()); - i.value()->deleteLater(); + if (i.value()) + { + removeCard(i.value()); + i.value()->deleteLater(); + } } // The pointers themselves were already deleted, so we don't delete them again. @@ -442,33 +552,54 @@ void CardDatabase::clear() simpleNameCards.clear(); QHashIterator setIt(sets); - while (setIt.hasNext()) { + while (setIt.hasNext()) + { setIt.next(); delete setIt.value(); } sets.clear(); loadStatus = NotLoaded; + + clearDatabaseMutex->unlock(); } void CardDatabase::addCard(CardInfo *card) { + if (card == nullptr) + { + qDebug() << "addCard(nullptr)"; + return; + } + + addCardMutex->lock(); cards.insert(card->getName(), card); simpleNameCards.insert(card->getSimpleName(), card); + addCardMutex->unlock(); emit cardAdded(card); } void CardDatabase::removeCard(CardInfo *card) { + if (card == nullptr) + { + qDebug() << "removeCard(nullptr)"; + return; + } + foreach(CardRelation * cardRelation, card->getRelatedCards()) cardRelation->deleteLater(); + foreach(CardRelation * cardRelation, card->getReverseRelatedCards()) cardRelation->deleteLater(); + foreach(CardRelation * cardRelation, card->getReverseRelatedCards2Me()) cardRelation->deleteLater(); + removeCardMutex->lock(); cards.remove(card->getName()); simpleNameCards.remove(card->getSimpleName()); + removeCardMutex->unlock(); emit cardRemoved(card); } @@ -494,8 +625,11 @@ CardInfo *CardDatabase::getCardBySimpleName(const QString &cardName) const CardSet *CardDatabase::getSet(const QString &setName) { if (sets.contains(setName)) + { return sets.value(setName); - else { + } + else + { CardSet *newSet = new CardSet(setName); sets.insert(setName, newSet); return newSet; @@ -506,7 +640,8 @@ SetList CardDatabase::getSetList() const { SetList result; QHashIterator i(sets); - while (i.hasNext()) { + while (i.hasNext()) + { i.next(); result << i.value(); } @@ -515,30 +650,48 @@ SetList CardDatabase::getSetList() const void CardDatabase::loadSetsFromXml(QXmlStreamReader &xml) { - while (!xml.atEnd()) { + while (!xml.atEnd()) + { if (xml.readNext() == QXmlStreamReader::EndElement) + { break; - if (xml.name() == "set") { + } + + if (xml.name() == "set") + { QString shortName, longName, setType; QDate releaseDate; - while (!xml.atEnd()) { + while (!xml.atEnd()) + { if (xml.readNext() == QXmlStreamReader::EndElement) + { break; + } + if (xml.name() == "name") + { shortName = xml.readElementText(); + } else if (xml.name() == "longname") + { longName = xml.readElementText(); + } else if (xml.name() == "settype") + { setType = xml.readElementText(); + } else if (xml.name() == "releasedate") + { releaseDate = QDate::fromString(xml.readElementText(), Qt::ISODate); - else if (xml.name() != "") { + } + else if (xml.name() != "") + { qDebug() << "[XMLReader] Unknown set property" << xml.name() << ", trying to continue anyway"; xml.skipCurrentElement(); } } - CardSet * newSet = getSet(shortName); + CardSet *newSet = getSet(shortName); newSet->setLongName(longName); newSet->setSetType(setType); newSet->setReleaseDate(releaseDate); @@ -548,10 +701,15 @@ void CardDatabase::loadSetsFromXml(QXmlStreamReader &xml) void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml) { - while (!xml.atEnd()) { + while (!xml.atEnd()) + { if (xml.readNext() == QXmlStreamReader::EndElement) + { break; - if (xml.name() == "card") { + } + + if (xml.name() == "card") + { QString name, manacost, cmc, type, pt, text; QStringList colors; QList relatedCards, reverseRelatedCards; @@ -564,81 +722,138 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml) bool cipt = false; bool isToken = false; bool upsideDown = false; - while (!xml.atEnd()) { + while (!xml.atEnd()) + { if (xml.readNext() == QXmlStreamReader::EndElement) + { break; + } + if (xml.name() == "name") + { name = xml.readElementText(); + } else if (xml.name() == "manacost") + { manacost = xml.readElementText(); + } else if (xml.name() == "cmc") + { cmc = xml.readElementText(); + } else if (xml.name() == "type") + { type = xml.readElementText(); + } else if (xml.name() == "pt") + { pt = xml.readElementText(); + } else if (xml.name() == "text") + { text = xml.readElementText(); - else if (xml.name() == "set") { + } + else if (xml.name() == "set") + { QXmlStreamAttributes attrs = xml.attributes(); QString setName = xml.readElementText(); sets.append(getSet(setName)); - if (attrs.hasAttribute("muId")) { + if (attrs.hasAttribute("muId")) + { muids[setName] = attrs.value("muId").toString().toInt(); } - if (attrs.hasAttribute("picURL")) { + + if (attrs.hasAttribute("picURL")) + { customPicURLs[setName] = attrs.value("picURL").toString(); } - if (attrs.hasAttribute("num")) { + + if (attrs.hasAttribute("num")) + { collectorNumbers[setName] = attrs.value("num").toString(); } - if (attrs.hasAttribute("rarity")) { + + if (attrs.hasAttribute("rarity")) + { rarities[setName] = attrs.value("rarity").toString(); } - } else if (xml.name() == "color") + } + else if (xml.name() == "color") + { colors << xml.readElementText(); - else if (xml.name() == "related" || xml.name() == "reverse-related") { + } + else if (xml.name() == "related" || xml.name() == "reverse-related") + { bool attach = false; bool exclude = false; bool variable = false; int count = 1; QXmlStreamAttributes attrs = xml.attributes(); QString cardName = xml.readElementText(); - if (attrs.hasAttribute("count")) { - if (attrs.value("count").toString().indexOf("x=") == 0) { + if (attrs.hasAttribute("count")) + { + if (attrs.value("count").toString().indexOf("x=") == 0) + { variable = true; count = attrs.value("count").toString().remove(0, 2).toInt(); } else if (attrs.value("count").toString().indexOf("x") == 0) + { variable = true; + } else + { count = attrs.value("count").toString().toInt(); + } + if (count < 1) + { count = 1; + } } - if (attrs.hasAttribute("attach")) { + + if (attrs.hasAttribute("attach")) + { attach = true; } - if (attrs.hasAttribute("exclude")) { + + if (attrs.hasAttribute("exclude")) + { exclude = true; } - CardRelation * relation = new CardRelation(cardName, attach, exclude, variable, count); - if (xml.name() == "reverse-related") { + + auto *relation = new CardRelation(cardName, attach, exclude, variable, count); + if (xml.name() == "reverse-related") + { reverseRelatedCards << relation; - } else { + } + else + { relatedCards << relation; } - } else if (xml.name() == "tablerow") + } + else if (xml.name() == "tablerow") + { tableRow = xml.readElementText().toInt(); + } else if (xml.name() == "cipt") + { cipt = (xml.readElementText() == "1"); + } else if (xml.name() == "upsidedown") + { upsideDown = (xml.readElementText() == "1"); + } else if (xml.name() == "loyalty") + { loyalty = xml.readElementText().toInt(); + } else if (xml.name() == "token") - isToken = xml.readElementText().toInt(); - else if (xml.name() != "") { + { + isToken = static_cast(xml.readElementText().toInt()); + } + else if (xml.name() != "") + { qDebug() << "[XMLReader] Unknown card property" << xml.name() << ", trying to continue anyway"; xml.skipCurrentElement(); } @@ -652,7 +867,9 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml) CardInfo *CardDatabase::getCardFromMap(const CardNameMap &cardMap, const QString &cardName) const { if (cardMap.contains(cardName)) + { return cardMap.value(cardName); + } return nullptr; } @@ -662,26 +879,44 @@ LoadStatus CardDatabase::loadFromFile(const QString &fileName) QFile file(fileName); file.open(QIODevice::ReadOnly); if (!file.isOpen()) + { return FileError; + } QXmlStreamReader xml(&file); - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::StartElement) { + while (!xml.atEnd()) + { + if (xml.readNext() == QXmlStreamReader::StartElement) + { if (xml.name() != "cockatrice_carddatabase") + { return Invalid; + } + int version = xml.attributes().value("version").toString().toInt(); - if (version < versionNeeded) { + if (version < versionNeeded) + { qDebug() << "[XMLReader] Version too old: " << version; return VersionTooOld; } - while (!xml.atEnd()) { + + while (!xml.atEnd()) + { if (xml.readNext() == QXmlStreamReader::EndElement) + { break; + } + if (xml.name() == "sets") + { loadSetsFromXml(xml); + } else if (xml.name() == "cards") + { loadCardsFromXml(xml); - else if (xml.name() != "") { + } + else if (xml.name() != "") + { qDebug() << "[XMLReader] Unknown item" << xml.name() << ", trying to continue anyway"; xml.skipCurrentElement(); } @@ -689,7 +924,10 @@ LoadStatus CardDatabase::loadFromFile(const QString &fileName) } } - if (cards.isEmpty()) return NoCards; + if (cards.isEmpty()) + { + return NoCards; + } return Ok; } @@ -698,7 +936,10 @@ bool CardDatabase::saveToFile(const QString &fileName, bool tokens) { QFile file(fileName); if (!file.open(QIODevice::WriteOnly)) + { return false; + } + QXmlStreamWriter xml(&file); xml.setAutoFormatting(true); @@ -706,19 +947,25 @@ bool CardDatabase::saveToFile(const QString &fileName, bool tokens) xml.writeStartElement("cockatrice_carddatabase"); xml.writeAttribute("version", QString::number(versionNeeded)); - if (!tokens) { + if (! tokens) + { xml.writeStartElement("sets"); QHashIterator setIterator(sets); while (setIterator.hasNext()) + { xml << setIterator.next().value(); + } + xml.writeEndElement(); // sets } xml.writeStartElement("cards"); QHashIterator cardIterator(cards); - while (cardIterator.hasNext()) { + while (cardIterator.hasNext()) + { CardInfo *card = cardIterator.next().value(); - if (tokens == card->getIsToken()) { + if (tokens == card->getIsToken()) + { xml << card; } } @@ -734,7 +981,11 @@ LoadStatus CardDatabase::loadCardDatabase(const QString &path) { LoadStatus tempLoadStatus = NotLoaded; if (!path.isEmpty()) + { + loadFromFileMutex->lock(); tempLoadStatus = loadFromFile(path); + loadFromFileMutex->unlock(); + } qDebug() << "[CardDatabase] loadCardDatabase(): Path =" << path << "Status =" << tempLoadStatus << "Cards =" << cards.size() << "Sets=" << sets.size(); @@ -743,13 +994,16 @@ LoadStatus CardDatabase::loadCardDatabase(const QString &path) LoadStatus CardDatabase::loadCardDatabases() { + reloadDatabaseMutex->lock(); + qDebug() << "CardDatabase::loadCardDatabases start"; - // clean old db - clear(); - // load main card database - loadStatus = loadCardDatabase(settingsCache->getCardDatabasePath()); - // laod tokens database - loadCardDatabase(settingsCache->getTokenDatabasePath()); + + clear(); // remove old db + + loadStatus = loadCardDatabase(settingsCache->getCardDatabasePath()); // load main card database + loadCardDatabase(settingsCache->getTokenDatabasePath()); // load tokens database + loadCardDatabase(settingsCache->getSpoilerCardDatabasePath()); // load spoilers database + // load custom card databases QDir dir(settingsCache->getCustomCardDatabasePath()); foreach(QString fileName, dir.entryList(QStringList("*.xml"), QDir::Files | QDir::Readable, QDir::Name | QDir::IgnoreCase)) @@ -763,25 +1017,26 @@ LoadStatus CardDatabase::loadCardDatabases() SetList allSets; QHashIterator setsIterator(sets); while (setsIterator.hasNext()) + { allSets.append(setsIterator.next().value()); + } allSets.sortByKey(); // resolve the reverse-related tags refreshCachedReverseRelatedCards(); - if(loadStatus == Ok) + if (loadStatus == Ok) { - // check for unknown sets - checkUnknownSets(); - // update deck editors, etc + checkUnknownSets(); // update deck editors, etc qDebug() << "CardDatabase::loadCardDatabases success"; - } else { - // bring up thr settings dialog + } + else + { qDebug() << "CardDatabase::loadCardDatabases failed"; - emit cardDatabaseLoadingFailed(); + emit cardDatabaseLoadingFailed(); // bring up the settings dialog } - // return the loadstatus of the main card database. + reloadDatabaseMutex->unlock(); return loadStatus; } @@ -792,21 +1047,30 @@ void CardDatabase::refreshCachedReverseRelatedCards() foreach(CardInfo * card, cards) { - if(card->getReverseRelatedCards().isEmpty()) + if (card->getReverseRelatedCards().isEmpty()) + { continue; + } QString relatedCardName; if (card->getPowTough().size() > 0) + { relatedCardName = card->getPowTough() + " " + card->getName(); // "n/n name" + } else + { relatedCardName = card->getName(); // "name" + } foreach(CardRelation * cardRelation, card->getReverseRelatedCards()) { const QString & targetCard = cardRelation->getName(); if (!cards.contains(targetCard)) + { continue; - CardRelation *newCardRelation = new CardRelation(relatedCardName, cardRelation->getDoesAttach(), + } + + auto *newCardRelation = new CardRelation(relatedCardName, cardRelation->getDoesAttach(), cardRelation->getIsCreateAllExclusion(), cardRelation->getIsVariable(), cardRelation->getDefaultCount()); @@ -819,13 +1083,20 @@ QStringList CardDatabase::getAllColors() const { QSet colors; QHashIterator cardIterator(cards); - while (cardIterator.hasNext()) { + while (cardIterator.hasNext()) + { const QStringList &cardColors = cardIterator.next().value()->getColors(); if (cardColors.isEmpty()) + { colors.insert("X"); + } else + { for (int i = 0; i < cardColors.size(); ++i) + { colors.insert(cardColors[i]); + } + } } return colors.toList(); } @@ -835,7 +1106,9 @@ QStringList CardDatabase::getAllMainCardTypes() const QSet types; QHashIterator cardIterator(cards); while (cardIterator.hasNext()) + { types.insert(cardIterator.next().value()->getMainCardType()); + } return types.toList(); } @@ -843,16 +1116,22 @@ void CardDatabase::checkUnknownSets() { SetList sets = getSetList(); - if(sets.getEnabledSetsNum()) + if (sets.getEnabledSetsNum()) { // if some sets are first found on thus run, ask the user int numUnknownSets = sets.getUnknownSetsNum(); QStringList unknownSetNames = sets.getUnknownSetsNames(); - if(numUnknownSets > 0) + if (numUnknownSets > 0) + { emit cardDatabaseNewSetsFound(numUnknownSets, unknownSetNames); + } else + { sets.markAllAsKnown(); - } else { + } + } + else + { // No set enabled. Probably this is the first time running trice sets.guessSortKeys(); sets.sortByKey(); @@ -887,11 +1166,14 @@ void CardDatabase::notifyEnabledSetsChanged() bool CardDatabase::saveCustomTokensToFile() { - CardSet * customTokensSet = getSet(CardDatabase::TOKENS_SETNAME); + CardSet *customTokensSet = getSet(CardDatabase::TOKENS_SETNAME); QString fileName = settingsCache->getCustomCardDatabasePath() + "/" + CardDatabase::TOKENS_SETNAME + ".xml"; QFile file(fileName); if (!file.open(QIODevice::WriteOnly)) + { return false; + } + QXmlStreamWriter xml(&file); xml.setAutoFormatting(true); @@ -901,10 +1183,13 @@ bool CardDatabase::saveCustomTokensToFile() xml.writeStartElement("cards"); QHashIterator cardIterator(cards); - while (cardIterator.hasNext()) { + while (cardIterator.hasNext()) + { CardInfo *card = cardIterator.next().value(); - if(card->getSets().contains(customTokensSet)) + if (card->getSets().contains(customTokensSet)) + { xml << card; + } } xml.writeEndElement(); // cards @@ -914,23 +1199,20 @@ bool CardDatabase::saveCustomTokensToFile() return true; } -CardRelation::CardRelation(const QString &_name, - bool _doesAttach, - bool _isCreateAllExclusion, - bool _isVariableCount, - int _defaultCount - ) - : name(_name), - doesAttach(_doesAttach), - isCreateAllExclusion(_isCreateAllExclusion), - isVariableCount(_isVariableCount), - defaultCount(_defaultCount) +CardRelation::CardRelation(const QString &_name, bool _doesAttach, bool _isCreateAllExclusion, bool _isVariableCount, int _defaultCount) : + name(_name), + doesAttach(_doesAttach), + isCreateAllExclusion(_isCreateAllExclusion), + isVariableCount(_isVariableCount), + defaultCount(_defaultCount) { } -void CardInfo::resetReverseRelatedCards2Me() { - foreach(CardRelation * cardRelation, this->getReverseRelatedCards2Me()) { +void CardInfo::resetReverseRelatedCards2Me() +{ + foreach(CardRelation * cardRelation, this->getReverseRelatedCards2Me()) + { cardRelation->deleteLater(); } reverseRelatedCardsToMe = QList(); diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index 800f22cb..a76bb598 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -8,6 +8,7 @@ #include #include #include +#include class CardDatabase; class CardInfo; @@ -18,159 +19,173 @@ typedef QMap QStringMap; // If we don't typedef this, CardInfo::CardInfo will refuse to compile on OS X < 10.9 typedef QMap MuidMap; -class CardSet : public QList { -private: - QString shortName, longName; - unsigned int sortKey; - QDate releaseDate; - QString setType; - bool enabled, isknown; -public: - CardSet(const QString &_shortName = QString(), const QString &_longName = QString(), const QString &_setType = QString(), const QDate &_releaseDate = QDate()); - QString getCorrectedShortName() const; - QString getShortName() const { return shortName; } - QString getLongName() const { return longName; } - QString getSetType() const { return setType; } - QDate getReleaseDate() const { return releaseDate; } - void setLongName(QString & _longName) { longName = _longName; } - void setSetType(QString & _setType) { setType = _setType; } - void setReleaseDate(QDate & _releaseDate) { releaseDate = _releaseDate; } +class CardSet : public QList +{ + private: + QString shortName, longName; + unsigned int sortKey; + QDate releaseDate; + QString setType; + bool enabled, isknown; - void loadSetOptions(); - int getSortKey() const { return sortKey; } - void setSortKey(unsigned int _sortKey); - bool getEnabled() const { return enabled; } - void setEnabled(bool _enabled); - bool getIsKnown() const { return isknown; } - void setIsKnown(bool _isknown); - //Determine incomplete sets. - bool getIsKnownIgnored() const { return longName.length() + setType.length() + releaseDate.toString().length() == 0 ; } + public: + explicit CardSet(const QString &_shortName = QString(), const QString &_longName = QString(), const QString &_setType = QString(), const QDate &_releaseDate = QDate()); + QString getCorrectedShortName() const; + QString getShortName() const { return shortName; } + QString getLongName() const { return longName; } + QString getSetType() const { return setType; } + QDate getReleaseDate() const { return releaseDate; } + void setLongName(QString & _longName) { longName = _longName; } + void setSetType(QString & _setType) { setType = _setType; } + void setReleaseDate(QDate & _releaseDate) { releaseDate = _releaseDate; } + + void loadSetOptions(); + int getSortKey() const { return sortKey; } + void setSortKey(unsigned int _sortKey); + bool getEnabled() const { return enabled; } + void setEnabled(bool _enabled); + bool getIsKnown() const { return isknown; } + void setIsKnown(bool _isknown); + + //Determine incomplete sets. + bool getIsKnownIgnored() const { return longName.length() + setType.length() + releaseDate.toString().length() == 0 ; } }; -class SetList : public QList { -private: - class KeyCompareFunctor; -public: - void sortByKey(); - void guessSortKeys(); - void enableAllUnknown(); - void enableAll(); - void markAllAsKnown(); - int getEnabledSetsNum(); - int getUnknownSetsNum(); - QStringList getUnknownSetsNames(); +class SetList : public QList +{ + private: + class KeyCompareFunctor; + + public: + void sortByKey(); + void guessSortKeys(); + void enableAllUnknown(); + void enableAll(); + void markAllAsKnown(); + int getEnabledSetsNum(); + int getUnknownSetsNum(); + QStringList getUnknownSetsNames(); }; -class CardInfo : public QObject { +class CardInfo : public QObject +{ Q_OBJECT -private: - QString name; + private: + QString name; - /* - * The name without punctuation or capitalization, for better card tag name - * recognition. - */ - QString simpleName; + /* + * The name without punctuation or capitalization, for better card tag name + * recognition. + */ + QString simpleName; - bool isToken; - SetList sets; - QString manacost; - QString cmc; - QString cardtype; - QString powtough; - QString text; - QStringList colors; - // the cards i'm related to - QList relatedCards; - // the card i'm reverse-related to - QList reverseRelatedCards; - // the cards thare are reverse-related to me - QList reverseRelatedCardsToMe; - QString setsNames; - bool upsideDownArt; - int loyalty; - QStringMap customPicURLs; - MuidMap muIds; - QStringMap collectorNumbers; - QStringMap rarities; - bool cipt; - int tableRow; - QString pixmapCacheKey; -public: - CardInfo(const QString &_name = QString(), - bool _isToken = false, - const QString &_manacost = QString(), - const QString &_cmc = QString(), - const QString &_cardtype = QString(), - const QString &_powtough = QString(), - const QString &_text = QString(), - const QStringList &_colors = QStringList(), - const QList &_relatedCards = QList(), - const QList &_reverseRelatedCards = QList(), - bool _upsideDownArt = false, - int _loyalty = 0, - bool _cipt = false, - int _tableRow = 0, - const SetList &_sets = SetList(), - const QStringMap &_customPicURLs = QStringMap(), - MuidMap muids = MuidMap(), - QStringMap _collectorNumbers = QStringMap(), - QStringMap _rarities = QStringMap() - ); - ~CardInfo(); - inline const QString &getName() const { return name; } - inline const QString &getSetsNames() const { return setsNames; } - const QString &getSimpleName() const { return simpleName; } - bool getIsToken() const { return isToken; } - const SetList &getSets() const { return sets; } - inline const QString &getManaCost() const { return manacost; } - inline const QString &getCmc() const { return cmc; } - inline const QString &getCardType() const { return cardtype; } - inline const QString &getPowTough() const { return powtough; } - const QString &getText() const { return text; } - const QString &getPixmapCacheKey() const { return pixmapCacheKey; } - const int &getLoyalty() const { return loyalty; } - bool getCipt() const { return cipt; } - void setManaCost(const QString &_manaCost) { manacost = _manaCost; emit cardInfoChanged(this); } - void setCmc(const QString &_cmc) { cmc = _cmc; emit cardInfoChanged(this); } - void setCardType(const QString &_cardType) { cardtype = _cardType; emit cardInfoChanged(this); } - void setPowTough(const QString &_powTough) { powtough = _powTough; emit cardInfoChanged(this); } - void setText(const QString &_text) { text = _text; emit cardInfoChanged(this); } - void setColors(const QStringList &_colors) { colors = _colors; emit cardInfoChanged(this); } - const QChar getColorChar() const; - const QStringList &getColors() const { return colors; } - const QList &getRelatedCards() const { return relatedCards; } - const QList &getReverseRelatedCards() const { return reverseRelatedCards; } - const QList &getReverseRelatedCards2Me() const { return reverseRelatedCardsToMe; } - void resetReverseRelatedCards2Me(); - void addReverseRelatedCards2Me(CardRelation * cardRelation) { reverseRelatedCardsToMe.append(cardRelation); } - bool getUpsideDownArt() const { return upsideDownArt; } - QString getCustomPicURL(const QString &set) const { return customPicURLs.value(set); } - int getMuId(const QString &set) const { return muIds.value(set); } - QString getCollectorNumber(const QString &set) const { return collectorNumbers.value(set); } - QString getRarity(const QString &set) const { return rarities.value(set); } - QStringMap getRarities() const { return rarities; } - QString getMainCardType() const; - QString getCorrectedName() const; - int getTableRow() const { return tableRow; } - void setTableRow(int _tableRow) { tableRow = _tableRow; } - void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(this); } - void setCustomPicURL(const QString &_set, const QString &_customPicURL) { customPicURLs.insert(_set, _customPicURL); } - void setMuId(const QString &_set, const int &_muId) { muIds.insert(_set, _muId); } - void setSetNumber(const QString &_set, const QString &_setNumber) { collectorNumbers.insert(_set, _setNumber); } - void setRarity(const QString &_set, const QString &_setNumber) { rarities.insert(_set, _setNumber); } - void addToSet(CardSet *set); - void emitPixmapUpdated() { emit pixmapUpdated(); } - void refreshCachedSetNames(); + bool isToken; + SetList sets; + QString manacost; + QString cmc; + QString cardtype; + QString powtough; + QString text; + QStringList colors; - /** - * Simplify a name to have no punctuation and lowercase all letters, for - * less strict name-matching. - */ - static QString simplifyName(const QString &name); -signals: - void pixmapUpdated(); - void cardInfoChanged(CardInfo *card); + // the cards i'm related to + QList relatedCards; + + // the card i'm reverse-related to + QList reverseRelatedCards; + + // the cards thare are reverse-related to me + QList reverseRelatedCardsToMe; + + QString setsNames; + + bool upsideDownArt; + int loyalty; + QStringMap customPicURLs; + MuidMap muIds; + QStringMap collectorNumbers; + QStringMap rarities; + bool cipt; + int tableRow; + QString pixmapCacheKey; + + public: + explicit CardInfo(const QString &_name = QString(), + bool _isToken = false, + const QString &_manacost = QString(), + const QString &_cmc = QString(), + const QString &_cardtype = QString(), + const QString &_powtough = QString(), + const QString &_text = QString(), + const QStringList &_colors = QStringList(), + const QList &_relatedCards = QList(), + const QList &_reverseRelatedCards = QList(), + bool _upsideDownArt = false, + int _loyalty = 0, + bool _cipt = false, + int _tableRow = 0, + const SetList &_sets = SetList(), + const QStringMap &_customPicURLs = QStringMap(), + MuidMap muids = MuidMap(), + QStringMap _collectorNumbers = QStringMap(), + QStringMap _rarities = QStringMap() + ); + ~CardInfo() override; + + inline const QString &getName() const { return name; } + inline const QString &getSetsNames() const { return setsNames; } + const QString &getSimpleName() const { return simpleName; } + bool getIsToken() const { return isToken; } + const SetList &getSets() const { return sets; } + inline const QString &getManaCost() const { return manacost; } + inline const QString &getCmc() const { return cmc; } + inline const QString &getCardType() const { return cardtype; } + inline const QString &getPowTough() const { return powtough; } + const QString &getText() const { return text; } + const QString &getPixmapCacheKey() const { return pixmapCacheKey; } + const int &getLoyalty() const { return loyalty; } + bool getCipt() const { return cipt; } + //void setManaCost(const QString &_manaCost) { manacost = _manaCost; emit cardInfoChanged(this); } + //void setCmc(const QString &_cmc) { cmc = _cmc; emit cardInfoChanged(this); } + void setCardType(const QString &_cardType) { cardtype = _cardType; emit cardInfoChanged(this); } + void setPowTough(const QString &_powTough) { powtough = _powTough; emit cardInfoChanged(this); } + void setText(const QString &_text) { text = _text; emit cardInfoChanged(this); } + void setColors(const QStringList &_colors) { colors = _colors; emit cardInfoChanged(this); } + const QChar getColorChar() const; + const QStringList &getColors() const { return colors; } + const QList &getRelatedCards() const { return relatedCards; } + const QList &getReverseRelatedCards() const { return reverseRelatedCards; } + const QList &getReverseRelatedCards2Me() const { return reverseRelatedCardsToMe; } + void resetReverseRelatedCards2Me(); + void addReverseRelatedCards2Me(CardRelation * cardRelation) { reverseRelatedCardsToMe.append(cardRelation); } + bool getUpsideDownArt() const { return upsideDownArt; } + QString getCustomPicURL(const QString &set) const { return customPicURLs.value(set); } + int getMuId(const QString &set) const { return muIds.value(set); } + QString getCollectorNumber(const QString &set) const { return collectorNumbers.value(set); } + QString getRarity(const QString &set) const { return rarities.value(set); } + QStringMap getRarities() const { return rarities; } + QString getMainCardType() const; + QString getCorrectedName() const; + int getTableRow() const { return tableRow; } + void setTableRow(int _tableRow) { tableRow = _tableRow; } + //void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(this); } + //void setCustomPicURL(const QString &_set, const QString &_customPicURL) { customPicURLs.insert(_set, _customPicURL); } + void setMuId(const QString &_set, const int &_muId) { muIds.insert(_set, _muId); } + void setSetNumber(const QString &_set, const QString &_setNumber) { collectorNumbers.insert(_set, _setNumber); } + void setRarity(const QString &_set, const QString &_setNumber) { rarities.insert(_set, _setNumber); } + void addToSet(CardSet *set); + void emitPixmapUpdated() { emit pixmapUpdated(); } + void refreshCachedSetNames(); + + /** + * Simplify a name to have no punctuation and lowercase all letters, for + * less strict name-matching. + */ + static QString simplifyName(const QString &name); + + signals: + void pixmapUpdated(); + void cardInfoChanged(CardInfo *card); }; enum LoadStatus { Ok, VersionTooOld, Invalid, NotLoaded, FileError, NoCards }; @@ -178,97 +193,106 @@ enum LoadStatus { Ok, VersionTooOld, Invalid, NotLoaded, FileError, NoCards }; typedef QHash CardNameMap; typedef QHash SetNameMap; -class CardDatabase : public QObject { +class CardDatabase : public QObject +{ Q_OBJECT -protected: - /* - * The cards, indexed by name. - */ - CardNameMap cards; + protected: + /* + * The cards, indexed by name. + */ + CardNameMap cards; - /** - * The cards, indexed by their simple name. - */ - CardNameMap simpleNameCards; + /** + * The cards, indexed by their simple name. + */ + CardNameMap simpleNameCards; - /* - * The sets, indexed by short name. - */ - SetNameMap sets; + /* + * The sets, indexed by short name. + */ + SetNameMap sets; - LoadStatus loadStatus; -private: - static const int versionNeeded; - void loadCardsFromXml(QXmlStreamReader &xml); - void loadSetsFromXml(QXmlStreamReader &xml); + LoadStatus loadStatus; + private: + static const int versionNeeded; + void loadCardsFromXml(QXmlStreamReader &xml); + void loadSetsFromXml(QXmlStreamReader &xml); - CardInfo *getCardFromMap(const CardNameMap &cardMap, const QString &cardName) const; - void checkUnknownSets(); - void refreshCachedReverseRelatedCards(); -public: - static const char* TOKENS_SETNAME; + CardInfo *getCardFromMap(const CardNameMap &cardMap, const QString &cardName) const; + void checkUnknownSets(); + void refreshCachedReverseRelatedCards(); - CardDatabase(QObject *parent = 0); - ~CardDatabase(); - void clear(); - void addCard(CardInfo *card); - void removeCard(CardInfo *card); - CardInfo *getCard(const QString &cardName) const; - QList getCards(const QStringList &cardNames) const; + QBasicMutex *reloadDatabaseMutex = new QBasicMutex(), + *clearDatabaseMutex = new QBasicMutex(), + *loadFromFileMutex = new QBasicMutex(), + *addCardMutex = new QBasicMutex(), + *removeCardMutex = new QBasicMutex(); - /* - * Get a card by its simple name. The name will be simplified in this - * function, so you don't need to simplify it beforehand. - */ - CardInfo *getCardBySimpleName(const QString &cardName) const; + public: + static const char* TOKENS_SETNAME; - CardSet *getSet(const QString &setName); - QList getCardList() const { return cards.values(); } - SetList getSetList() const; - LoadStatus loadFromFile(const QString &fileName); - bool saveToFile(const QString &fileName, bool tokens = false); - bool saveCustomTokensToFile(); - QStringList getAllColors() const; - QStringList getAllMainCardTypes() const; - LoadStatus getLoadStatus() const { return loadStatus; } - void enableAllUnknownSets(); - void markAllSetsAsKnown(); - void notifyEnabledSetsChanged(); + explicit CardDatabase(QObject *parent = nullptr); + ~CardDatabase() override; + void clear(); + void addCard(CardInfo *card); + void removeCard(CardInfo *card); + CardInfo *getCard(const QString &cardName) const; + QList getCards(const QStringList &cardNames) const; -public slots: - LoadStatus loadCardDatabases(); -private slots: - LoadStatus loadCardDatabase(const QString &path); -signals: - void cardDatabaseLoadingFailed(); - void cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknownSetsNames); - void cardDatabaseAllNewSetsEnabled(); - void cardDatabaseEnabledSetsChanged(); - void cardAdded(CardInfo *card); - void cardRemoved(CardInfo *card); + /* + * Get a card by its simple name. The name will be simplified in this + * function, so you don't need to simplify it beforehand. + */ + CardInfo *getCardBySimpleName(const QString &cardName) const; + + CardSet *getSet(const QString &setName); + QList getCardList() const { return cards.values(); } + SetList getSetList() const; + LoadStatus loadFromFile(const QString &fileName); + bool saveToFile(const QString &fileName, bool tokens = false); + bool saveCustomTokensToFile(); + QStringList getAllColors() const; + QStringList getAllMainCardTypes() const; + LoadStatus getLoadStatus() const { return loadStatus; } + void enableAllUnknownSets(); + void markAllSetsAsKnown(); + void notifyEnabledSetsChanged(); + + public slots: + LoadStatus loadCardDatabases(); + private slots: + LoadStatus loadCardDatabase(const QString &path); + signals: + void cardDatabaseLoadingFailed(); + void cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknownSetsNames); + void cardDatabaseAllNewSetsEnabled(); + void cardDatabaseEnabledSetsChanged(); + void cardAdded(CardInfo *card); + void cardRemoved(CardInfo *card); }; -class CardRelation : public QObject { +class CardRelation : public QObject +{ Q_OBJECT -private: - QString name; - bool doesAttach; - bool isCreateAllExclusion; - bool isVariableCount; - int defaultCount; -public: - CardRelation(const QString &_name = QString(), - bool _doesAttach = false, - bool _isCreateAllExclusion = false, - bool _isVariableCount = false, - int _defaultCount = 1 - ); - inline const QString &getName() const { return name; } - bool getDoesAttach() const { return doesAttach; } - bool getCanCreateAnother() const { return !doesAttach; } - bool getIsCreateAllExclusion() const { return isCreateAllExclusion; } - bool getIsVariable() const { return isVariableCount; } - int getDefaultCount() const { return defaultCount; } -}; + private: + QString name; + bool doesAttach; + bool isCreateAllExclusion; + bool isVariableCount; + int defaultCount; + public: + explicit CardRelation(const QString &_name = QString(), + bool _doesAttach = false, + bool _isCreateAllExclusion = false, + bool _isVariableCount = false, + int _defaultCount = 1 + ); -#endif + inline const QString &getName() const { return name; } + bool getDoesAttach() const { return doesAttach; } + bool getCanCreateAnother() const { return !doesAttach; } + bool getIsCreateAllExclusion() const { return isCreateAllExclusion; } + bool getIsVariable() const { return isVariableCount; } + int getDefaultCount() const { return defaultCount; } +}; +#endif \ No newline at end of file diff --git a/cockatrice/src/dlg_connect.cpp b/cockatrice/src/dlg_connect.cpp index ce32fcb4..b3e46ac0 100644 --- a/cockatrice/src/dlg_connect.cpp +++ b/cockatrice/src/dlg_connect.cpp @@ -16,8 +16,7 @@ #define PUBLIC_SERVERS_URL "https://github.com/Cockatrice/Cockatrice/wiki/Public-Servers" -DlgConnect::DlgConnect(QWidget *parent) - : QDialog(parent) +DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent) { previousHostButton = new QRadioButton(tr("Known Hosts"), this); previousHosts = new QComboBox(this); @@ -64,7 +63,7 @@ DlgConnect::DlgConnect(QWidget *parent) if (savePasswordCheckBox->isChecked()) { - autoConnectCheckBox->setChecked(settingsCache->servers().getAutoConnect()); + autoConnectCheckBox->setChecked(static_cast(settingsCache->servers().getAutoConnect())); autoConnectCheckBox->setEnabled(true); } else @@ -87,11 +86,11 @@ DlgConnect::DlgConnect(QWidget *parent) btnCancel->setFixedWidth(100); connect(btnCancel, SIGNAL(released()), this, SLOT(actCancel())); - QGridLayout *newHostLayout = new QGridLayout; + auto *newHostLayout = new QGridLayout; newHostLayout->addWidget(newHostButton, 0, 1); newHostLayout->addWidget(publicServersLabel, 0, 2); - QGridLayout *connectionLayout = new QGridLayout; + auto *connectionLayout = new QGridLayout; connectionLayout->addWidget(previousHostButton, 0, 1); connectionLayout->addWidget(previousHosts, 1, 1); connectionLayout->addLayout(newHostLayout, 2, 1, 1, 2); @@ -103,7 +102,7 @@ DlgConnect::DlgConnect(QWidget *parent) connectionLayout->addWidget(portEdit, 5, 1); connectionLayout->addWidget(autoConnectCheckBox, 6, 1); - QGridLayout *buttons = new QGridLayout; + auto *buttons = new QGridLayout; buttons->addWidget(btnOk, 0, 0); buttons->addWidget(btnForgotPassword, 0, 1); buttons->addWidget(btnCancel, 0, 2); @@ -111,7 +110,7 @@ DlgConnect::DlgConnect(QWidget *parent) QGroupBox *restrictionsGroupBox = new QGroupBox(tr("Server")); restrictionsGroupBox->setLayout(connectionLayout); - QGridLayout *loginLayout = new QGridLayout; + auto *loginLayout = new QGridLayout; loginLayout->addWidget(playernameLabel, 0, 0); loginLayout->addWidget(playernameEdit, 0, 1); loginLayout->addWidget(passwordLabel, 1, 0); @@ -124,12 +123,12 @@ DlgConnect::DlgConnect(QWidget *parent) QGroupBox *btnGroupBox = new QGroupBox(tr("")); btnGroupBox->setLayout(buttons); - QGridLayout *grid = new QGridLayout; + auto *grid = new QGridLayout; grid->addWidget(restrictionsGroupBox, 0, 0); grid->addWidget(loginGroupBox, 1, 0); grid->addWidget(btnGroupBox, 2, 0); - - QVBoxLayout *mainLayout = new QVBoxLayout; + + auto *mainLayout = new QVBoxLayout; mainLayout->addLayout(grid); setLayout(mainLayout); @@ -289,9 +288,9 @@ void DlgConnect::actCancel() bool DeleteHighlightedItemWhenShiftDelPressedEventFilter::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::KeyPress) { - QKeyEvent *keyEvent = static_cast(event); + auto *keyEvent = dynamic_cast(event); if (keyEvent->key() == Qt::Key_Delete) { - QComboBox* combobox = reinterpret_cast(obj); + auto *combobox = reinterpret_cast(obj); combobox->removeItem(combobox->currentIndex()); return true; } diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index a87c3cb2..814b6dae 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -30,6 +30,7 @@ #include "releasechannel.h" #include "soundengine.h" #include "sequenceEdit/shortcutstab.h" +#include "spoilerbackgroundupdater.h" #define WIKI_CUSTOM_PIC_URL "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Picture-Download-URLs" @@ -63,7 +64,7 @@ GeneralSettingsPage::GeneralSettingsPage() pixmapCacheEdit.setSingleStep(64); pixmapCacheEdit.setValue(settingsCache->getPixmapCacheSize()); pixmapCacheEdit.setSuffix(" MB"); - + defaultUrlEdit = new QLineEdit(settingsCache->getPicUrl()); fallbackUrlEdit = new QLineEdit(settingsCache->getPicUrlFallback()); @@ -81,7 +82,7 @@ GeneralSettingsPage::GeneralSettingsPage() setEnabledStatus(settingsCache->getPicDownload()); - QGridLayout *personalGrid = new QGridLayout; + auto *personalGrid = new QGridLayout; personalGrid->addWidget(&languageLabel, 0, 0); personalGrid->addWidget(&languageBox, 0, 1); personalGrid->addWidget(&updateReleaseChannelLabel, 1, 0); @@ -98,7 +99,7 @@ GeneralSettingsPage::GeneralSettingsPage() personalGrid->addWidget(&fallbackUrlRestoreButton, 6, 2, 1, 1); personalGrid->addWidget(&urlLinkLabel, 7, 1, 1, 1); personalGrid->addWidget(&clearDownloadedPicsButton, 8, 0, 1, 3); - + urlLinkLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); urlLinkLabel.setOpenExternalLinks(true); @@ -114,23 +115,23 @@ GeneralSettingsPage::GeneralSettingsPage() replaysPathEdit->setReadOnly(true); QPushButton *replaysPathButton = new QPushButton("..."); connect(replaysPathButton, SIGNAL(clicked()), this, SLOT(replaysPathButtonClicked())); - + picsPathEdit = new QLineEdit(settingsCache->getPicsPath()); picsPathEdit->setReadOnly(true); QPushButton *picsPathButton = new QPushButton("..."); connect(picsPathButton, SIGNAL(clicked()), this, SLOT(picsPathButtonClicked())); - + cardDatabasePathEdit = new QLineEdit(settingsCache->getCardDatabasePath()); cardDatabasePathEdit->setReadOnly(true); QPushButton *cardDatabasePathButton = new QPushButton("..."); connect(cardDatabasePathButton, SIGNAL(clicked()), this, SLOT(cardDatabasePathButtonClicked())); - + tokenDatabasePathEdit = new QLineEdit(settingsCache->getTokenDatabasePath()); tokenDatabasePathEdit->setReadOnly(true); QPushButton *tokenDatabasePathButton = new QPushButton("..."); connect(tokenDatabasePathButton, SIGNAL(clicked()), this, SLOT(tokenDatabasePathButtonClicked())); - - if(settingsCache->getIsPortableBuild()) + + if (settingsCache->getIsPortableBuild()) { deckPathEdit->setEnabled(false); replaysPathEdit->setEnabled(false); @@ -145,7 +146,7 @@ GeneralSettingsPage::GeneralSettingsPage() tokenDatabasePathButton->setVisible(false); } - QGridLayout *pathsGrid = new QGridLayout; + auto *pathsGrid = new QGridLayout; pathsGrid->addWidget(&deckPathLabel, 0, 0); pathsGrid->addWidget(deckPathEdit, 0, 1); pathsGrid->addWidget(deckPathButton, 0, 2); @@ -164,10 +165,10 @@ GeneralSettingsPage::GeneralSettingsPage() pathsGroupBox = new QGroupBox; pathsGroupBox->setLayout(pathsGrid); - QVBoxLayout *mainLayout = new QVBoxLayout; + auto *mainLayout = new QVBoxLayout; mainLayout->addWidget(personalGroupBox); mainLayout->addWidget(pathsGroupBox); - + setLayout(mainLayout); } @@ -186,7 +187,7 @@ QString GeneralSettingsPage::languageName(const QString &qmFile) QTranslator translator; translator.load(translationPrefix + "_" + qmFile + ".qm", translationPath); - + return translator.translate("i18n", DEFAULT_LANG_NAME); } @@ -209,7 +210,7 @@ void GeneralSettingsPage::deckPathButtonClicked() QString path = QFileDialog::getExistingDirectory(this, tr("Choose path")); if (path.isEmpty()) return; - + deckPathEdit->setText(path); settingsCache->setDeckPath(path); } @@ -219,7 +220,7 @@ void GeneralSettingsPage::replaysPathButtonClicked() QString path = QFileDialog::getExistingDirectory(this, tr("Choose path")); if (path.isEmpty()) return; - + replaysPathEdit->setText(path); settingsCache->setReplaysPath(path); } @@ -229,7 +230,7 @@ void GeneralSettingsPage::picsPathButtonClicked() QString path = QFileDialog::getExistingDirectory(this, tr("Choose path")); if (path.isEmpty()) return; - + picsPathEdit->setText(path); settingsCache->setPicsPath(path); } @@ -263,7 +264,7 @@ void GeneralSettingsPage::cardDatabasePathButtonClicked() QString path = QFileDialog::getOpenFileName(this, tr("Choose path")); if (path.isEmpty()) return; - + cardDatabasePathEdit->setText(path); settingsCache->setCardDatabasePath(path); } @@ -273,7 +274,7 @@ void GeneralSettingsPage::tokenDatabasePathButtonClicked() QString path = QFileDialog::getOpenFileName(this, tr("Choose path")); if (path.isEmpty()) return; - + tokenDatabasePathEdit->setText(path); settingsCache->setTokenDatabasePath(path); } @@ -332,8 +333,8 @@ AppearanceSettingsPage::AppearanceSettingsPage() } connect(&themeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(themeBoxChanged(int))); - - QGridLayout *themeGrid = new QGridLayout; + + auto *themeGrid = new QGridLayout; themeGrid->addWidget(&themeLabel, 0, 0); themeGrid->addWidget(&themeBox, 0, 1); @@ -345,30 +346,30 @@ AppearanceSettingsPage::AppearanceSettingsPage() cardScalingCheckBox.setChecked(settingsCache->getScaleCards()); connect(&cardScalingCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setCardScaling(int))); - - QGridLayout *cardsGrid = new QGridLayout; + + auto *cardsGrid = new QGridLayout; cardsGrid->addWidget(&displayCardNamesCheckBox, 0, 0, 1, 2); cardsGrid->addWidget(&cardScalingCheckBox, 1, 0, 1, 2); - + cardsGroupBox = new QGroupBox; cardsGroupBox->setLayout(cardsGrid); - + horizontalHandCheckBox.setChecked(settingsCache->getHorizontalHand()); connect(&horizontalHandCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setHorizontalHand(int))); leftJustifiedHandCheckBox.setChecked(settingsCache->getLeftJustified()); connect(&leftJustifiedHandCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setLeftJustified(int))); - - QGridLayout *handGrid = new QGridLayout; + + auto *handGrid = new QGridLayout; handGrid->addWidget(&horizontalHandCheckBox, 0, 0, 1, 2); handGrid->addWidget(&leftJustifiedHandCheckBox, 1, 0, 1, 2); - + handGroupBox = new QGroupBox; handGroupBox->setLayout(handGrid); - + invertVerticalCoordinateCheckBox.setChecked(settingsCache->getInvertVerticalCoordinate()); connect(&invertVerticalCoordinateCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setInvertVerticalCoordinate(int))); - + minPlayersForMultiColumnLayoutEdit.setMinimum(2); minPlayersForMultiColumnLayoutEdit.setValue(settingsCache->getMinPlayersForMultiColumnLayout()); connect(&minPlayersForMultiColumnLayoutEdit, SIGNAL(valueChanged(int)), settingsCache, SLOT(setMinPlayersForMultiColumnLayout(int))); @@ -380,22 +381,22 @@ AppearanceSettingsPage::AppearanceSettingsPage() maxFontSizeForCardsEdit.setMinimum(9); maxFontSizeForCardsEdit.setMaximum(100); - QGridLayout *tableGrid = new QGridLayout; + auto *tableGrid = new QGridLayout; tableGrid->addWidget(&invertVerticalCoordinateCheckBox, 0, 0, 1, 2); tableGrid->addWidget(&minPlayersForMultiColumnLayoutLabel, 1, 0, 1, 1); tableGrid->addWidget(&minPlayersForMultiColumnLayoutEdit, 1, 1, 1, 1); tableGrid->addWidget(&maxFontSizeForCardsLabel, 2, 0, 1, 1); tableGrid->addWidget(&maxFontSizeForCardsEdit, 2, 1, 1, 1); - + tableGroupBox = new QGroupBox; tableGroupBox->setLayout(tableGrid); - - QVBoxLayout *mainLayout = new QVBoxLayout; + + auto *mainLayout = new QVBoxLayout; mainLayout->addWidget(themeGroupBox); mainLayout->addWidget(cardsGroupBox); mainLayout->addWidget(handGroupBox); mainLayout->addWidget(tableGroupBox); - + setLayout(mainLayout); } @@ -410,15 +411,15 @@ void AppearanceSettingsPage::retranslateUi() { themeGroupBox->setTitle(tr("Theme settings")); themeLabel.setText(tr("Current theme:")); - + cardsGroupBox->setTitle(tr("Card rendering")); displayCardNamesCheckBox.setText(tr("Display card names on cards having a picture")); cardScalingCheckBox.setText(tr("Scale cards on mouse over")); - + handGroupBox->setTitle(tr("Hand layout")); horizontalHandCheckBox.setText(tr("Display hand horizontally (wastes space)")); leftJustifiedHandCheckBox.setText(tr("Enable left justification")); - + tableGroupBox->setTitle(tr("Table grid layout")); invertVerticalCoordinateCheckBox.setText(tr("Invert vertical coordinate")); minPlayersForMultiColumnLayoutLabel.setText(tr("Minimum player count for multi-column layout:")); @@ -437,36 +438,36 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() doubleClickToPlayCheckBox.setChecked(settingsCache->getDoubleClickToPlay()); connect(&doubleClickToPlayCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setDoubleClickToPlay(int))); - + playToStackCheckBox.setChecked(settingsCache->getPlayToStack()); connect(&playToStackCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPlayToStack(int))); annotateTokensCheckBox.setChecked(settingsCache->getAnnotateTokens()); connect(&annotateTokensCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setAnnotateTokens(int))); - QGridLayout *generalGrid = new QGridLayout; + auto *generalGrid = new QGridLayout; generalGrid->addWidget(¬ificationsEnabledCheckBox, 0, 0); generalGrid->addWidget(&specNotificationsEnabledCheckBox, 1, 0); generalGrid->addWidget(&doubleClickToPlayCheckBox, 2, 0); generalGrid->addWidget(&playToStackCheckBox, 3, 0); generalGrid->addWidget(&annotateTokensCheckBox, 4, 0); - + generalGroupBox = new QGroupBox; generalGroupBox->setLayout(generalGrid); - + tapAnimationCheckBox.setChecked(settingsCache->getTapAnimation()); connect(&tapAnimationCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setTapAnimation(int))); - - QGridLayout *animationGrid = new QGridLayout; + + auto *animationGrid = new QGridLayout; animationGrid->addWidget(&tapAnimationCheckBox, 0, 0); - + animationGroupBox = new QGroupBox; animationGroupBox->setLayout(animationGrid); - QVBoxLayout *mainLayout = new QVBoxLayout; + auto *mainLayout = new QVBoxLayout; mainLayout->addWidget(generalGroupBox); mainLayout->addWidget(animationGroupBox); - + setLayout(mainLayout); } @@ -486,25 +487,123 @@ void UserInterfaceSettingsPage::retranslateUi() tapAnimationCheckBox.setText(tr("&Tap/untap animation")); } - DeckEditorSettingsPage::DeckEditorSettingsPage() { - QGridLayout *generalGrid = new QGridLayout; - - generalGrid->addWidget(new QLabel(tr("Nothing is here... yet")), 0, 0); - - generalGroupBox = new QGroupBox; - generalGroupBox->setLayout(generalGrid); - - QVBoxLayout *mainLayout = new QVBoxLayout; - mainLayout->addWidget(generalGroupBox); - - setLayout(mainLayout); + auto *lpGeneralGrid = new QGridLayout; + auto *lpSpoilerGrid = new QGridLayout; + + mcDownloadSpoilersCheckBox.setChecked(settingsCache->getDownloadSpoilersStatus()); + + mpSpoilerSavePathLineEdit = new QLineEdit(settingsCache->getSpoilerCardDatabasePath()); + mpSpoilerSavePathLineEdit->setReadOnly(true); + mpSpoilerPathButton = new QPushButton("..."); + connect(mpSpoilerPathButton, SIGNAL(clicked()), this, SLOT(spoilerPathButtonClicked())); + + updateNowButton = new QPushButton(tr("Update Spoilers")); + connect(updateNowButton, SIGNAL(clicked()), this, SLOT(updateSpoilers())); + + // Update the GUI depending on if the box is ticked or not + setSpoilersEnabled(mcDownloadSpoilersCheckBox.isChecked()); + + // Create the layout + lpGeneralGrid->addWidget(&mcGeneralMessageLabel, 0, 0); + + lpSpoilerGrid->addWidget(&mcDownloadSpoilersCheckBox, 0, 0); + lpSpoilerGrid->addWidget(updateNowButton, 0, 2); + lpSpoilerGrid->addWidget(&mcSpoilerSaveLabel, 1, 0); + lpSpoilerGrid->addWidget(mpSpoilerSavePathLineEdit, 1, 1); + lpSpoilerGrid->addWidget(mpSpoilerPathButton, 1, 2); + lpSpoilerGrid->addWidget(&infoOnSpoilersLabel, 2, 0, 1, 3, Qt::AlignTop); + + // On a change to the check box, hide/unhide the other fields + connect(&mcDownloadSpoilersCheckBox, SIGNAL(toggled(bool)), settingsCache, SLOT(setDownloadSpoilerStatus(bool))); + connect(&mcDownloadSpoilersCheckBox, SIGNAL(toggled(bool)), this, SLOT(setSpoilersEnabled(bool))); + + mpGeneralGroupBox = new QGroupBox; + mpGeneralGroupBox->setLayout(lpGeneralGrid); + + mpSpoilerGroupBox = new QGroupBox; + mpSpoilerGroupBox->setLayout(lpSpoilerGrid); + + auto *lpMainLayout = new QVBoxLayout; + lpMainLayout->addWidget(mpGeneralGroupBox); + lpMainLayout->addWidget(mpSpoilerGroupBox); + + setLayout(lpMainLayout); +} + +void DeckEditorSettingsPage::updateSpoilers() +{ + // Disable the button so the user can only press it once at a time + updateNowButton->setDisabled(true); + updateNowButton->setText(tr("Updating Spoilers")); + + // Create a new SBU that will act as if the client was just reloaded + auto *sbu = new SpoilerBackgroundUpdater(); + connect(sbu, SIGNAL(spoilerCheckerDone()), this, SLOT(unlockSettings())); + connect(sbu, SIGNAL(spoilersUpdatedSuccessfully()), this, SLOT(unlockSettings())); +} + +void DeckEditorSettingsPage::unlockSettings() +{ + updateNowButton->setDisabled(false); + updateNowButton->setText(tr("Update Spoilers")); +} + +QString DeckEditorSettingsPage::getLastUpdateTime() +{ + QString fileName = settingsCache->getSpoilerCardDatabasePath(); + QFileInfo fi(fileName); + QDir fileDir(fi.path()); + QFile file(fileName); + + if (file.exists()) + { + return fi.lastModified().toString("MMM d, hh:mm"); + } + + return QString(); +} + +void DeckEditorSettingsPage::spoilerPathButtonClicked() +{ + QString lsPath = QFileDialog::getExistingDirectory(this, tr("Choose path")); + if (lsPath.isEmpty()) + { + return; + } + + mpSpoilerSavePathLineEdit->setText(lsPath + "/spoiler.xml"); + settingsCache->setSpoilerDatabasePath(lsPath + "/spoiler.xml"); +} + +void DeckEditorSettingsPage::setSpoilersEnabled(bool anInput) +{ + msDownloadSpoilersLabel.setEnabled(anInput); + mcSpoilerSaveLabel.setEnabled(anInput); + mpSpoilerSavePathLineEdit->setEnabled(anInput); + mpSpoilerPathButton->setEnabled(anInput); + updateNowButton->setEnabled(anInput); + infoOnSpoilersLabel.setEnabled(anInput); + + if (! anInput) + { + SpoilerBackgroundUpdater::deleteSpoilerFile(); + } } void DeckEditorSettingsPage::retranslateUi() { - generalGroupBox->setTitle(tr("General")); + mpSpoilerGroupBox->setTitle(tr("Spoilers")); + mcDownloadSpoilersCheckBox.setText(tr("Download Spoilers Automatically")); + mcSpoilerSaveLabel.setText(tr("Spoiler Location:")); + mcGeneralMessageLabel.setText(tr("Hey, something's here finally!")); + infoOnSpoilersLabel.setText( + tr("Last Updated") + ": " + getLastUpdateTime() + "\n\n" + + tr("Spoilers download automatically on launch") + "\n" + + tr("Press the button to manually update without relaunching") + "\n\n" + + tr("Do not close settings until manual update complete") + ); } MessagesSettingsPage::MessagesSettingsPage() @@ -514,12 +613,12 @@ MessagesSettingsPage::MessagesSettingsPage() chatMentionCompleterCheckbox.setChecked(settingsCache->getChatMentionCompleter()); connect(&chatMentionCompleterCheckbox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setChatMentionCompleter(int))); - + ignoreUnregUsersMainChat.setChecked(settingsCache->getIgnoreUnregisteredUsers()); ignoreUnregUserMessages.setChecked(settingsCache->getIgnoreUnregisteredUserMessages()); connect(&ignoreUnregUsersMainChat, SIGNAL(stateChanged(int)), settingsCache, SLOT(setIgnoreUnregisteredUsers(int))); connect(&ignoreUnregUserMessages, SIGNAL(stateChanged(int)), settingsCache, SLOT(setIgnoreUnregisteredUserMessages(int))); - + invertMentionForeground.setChecked(settingsCache->getChatMentionForeground()); connect(&invertMentionForeground, SIGNAL(stateChanged(int)), this, SLOT(updateTextColor(int))); @@ -545,7 +644,7 @@ MessagesSettingsPage::MessagesSettingsPage() customAlertString->setText(settingsCache->getHighlightWords()); connect(customAlertString, SIGNAL(textChanged(QString)), settingsCache, SLOT(setHighlightWords(QString))); - QGridLayout *chatGrid = new QGridLayout; + auto *chatGrid = new QGridLayout; chatGrid->addWidget(&chatMentionCheckBox, 0, 0); chatGrid->addWidget(&invertMentionForeground, 0, 1); chatGrid->addWidget(mentionColor, 0, 2); @@ -558,13 +657,13 @@ MessagesSettingsPage::MessagesSettingsPage() chatGrid->addWidget(&roomHistory, 6, 0); chatGroupBox = new QGroupBox; chatGroupBox->setLayout(chatGrid); - + highlightColor = new QLineEdit(); highlightColor->setText(settingsCache->getChatHighlightColor()); updateHighlightPreview(); connect(highlightColor, SIGNAL(textChanged(QString)), this, SLOT(updateHighlightColor(QString))); - QGridLayout *highlightNotice = new QGridLayout; + auto *highlightNotice = new QGridLayout; highlightNotice->addWidget(highlightColor, 0, 2); highlightNotice->addWidget(&invertHighlightForeground, 0, 1); highlightNotice->addWidget(&hexHighlightLabel, 1, 2); @@ -578,7 +677,7 @@ MessagesSettingsPage::MessagesSettingsPage() int count = settingsCache->messages().getCount(); for (int i = 0; i < count; i++) messageList->addItem(settingsCache->messages().getMessageAt(i)); - + aAdd = new QAction(this); aAdd->setIcon(QPixmap("theme:icons/increment")); connect(aAdd, SIGNAL(triggered()), this, SLOT(actAdd())); @@ -586,30 +685,31 @@ MessagesSettingsPage::MessagesSettingsPage() aRemove->setIcon(QPixmap("theme:icons/decrement")); connect(aRemove, SIGNAL(triggered()), this, SLOT(actRemove())); - QToolBar *messageToolBar = new QToolBar; + auto *messageToolBar = new QToolBar; messageToolBar->setOrientation(Qt::Vertical); messageToolBar->addAction(aAdd); messageToolBar->addAction(aRemove); - QHBoxLayout *messageListLayout = new QHBoxLayout; + auto *messageListLayout = new QHBoxLayout; messageListLayout->addWidget(messageToolBar); messageListLayout->addWidget(messageList); messageShortcuts = new QGroupBox; messageShortcuts->setLayout(messageListLayout); - QVBoxLayout *mainLayout = new QVBoxLayout; + auto *mainLayout = new QVBoxLayout; mainLayout->addWidget(messageShortcuts); mainLayout->addWidget(chatGroupBox); mainLayout->addWidget(highlightGroupBox); setLayout(mainLayout); - + retranslateUi(); } -void MessagesSettingsPage::updateColor(const QString &value) { +void MessagesSettingsPage::updateColor(const QString &value) +{ QColor colorToSet; colorToSet.setNamedColor("#" + value); if (colorToSet.isValid()) { @@ -618,7 +718,8 @@ void MessagesSettingsPage::updateColor(const QString &value) { } } -void MessagesSettingsPage::updateHighlightColor(const QString &value) { +void MessagesSettingsPage::updateHighlightColor(const QString &value) +{ QColor colorToSet; colorToSet.setNamedColor("#" + value); if (colorToSet.isValid()) { @@ -627,22 +728,26 @@ void MessagesSettingsPage::updateHighlightColor(const QString &value) { } } -void MessagesSettingsPage::updateTextColor(int value) { +void MessagesSettingsPage::updateTextColor(int value) +{ settingsCache->setChatMentionForeground(value); updateMentionPreview(); } -void MessagesSettingsPage::updateTextHighlightColor(int value) { +void MessagesSettingsPage::updateTextHighlightColor(int value) +{ settingsCache->setChatHighlightForeground(value); updateHighlightPreview(); } -void MessagesSettingsPage::updateMentionPreview() { - mentionColor->setStyleSheet("QLineEdit{background:#" + settingsCache->getChatMentionColor() + +void MessagesSettingsPage::updateMentionPreview() +{ + mentionColor->setStyleSheet("QLineEdit{background:#" + settingsCache->getChatMentionColor() + ";color: " + (settingsCache->getChatMentionForeground() ? "white" : "black") + ";}"); } -void MessagesSettingsPage::updateHighlightPreview() { +void MessagesSettingsPage::updateHighlightPreview() +{ highlightColor->setStyleSheet("QLineEdit{background:#" + settingsCache->getChatHighlightColor() + ";color: " + (settingsCache->getChatHighlightForeground() ? "white" : "black") + ";}"); } @@ -725,7 +830,7 @@ SoundSettingsPage::SoundSettingsPage() connect(masterVolumeSlider, SIGNAL(valueChanged(int)), masterVolumeSpinBox, SLOT(setValue(int))); connect(masterVolumeSpinBox, SIGNAL(valueChanged(int)), masterVolumeSlider, SLOT(setValue(int))); - QGridLayout *soundGrid = new QGridLayout; + auto *soundGrid = new QGridLayout; soundGrid->addWidget(&soundEnabledCheckBox, 0, 0, 1, 3); soundGrid->addWidget(&masterVolumeLabel, 1, 0); soundGrid->addWidget(masterVolumeSlider, 1, 1); @@ -737,7 +842,7 @@ SoundSettingsPage::SoundSettingsPage() soundGroupBox = new QGroupBox; soundGroupBox->setLayout(soundGrid); - QVBoxLayout *mainLayout = new QVBoxLayout; + auto *mainLayout = new QVBoxLayout; mainLayout->addWidget(soundGroupBox); setLayout(mainLayout); @@ -750,27 +855,28 @@ void SoundSettingsPage::themeBoxChanged(int index) settingsCache->setSoundThemeName(themeDirs.at(index)); } -void SoundSettingsPage::masterVolumeChanged(int value) { +void SoundSettingsPage::masterVolumeChanged(int value) +{ masterVolumeSlider->setToolTip(QString::number(value)); } -void SoundSettingsPage::retranslateUi() { +void SoundSettingsPage::retranslateUi() +{ soundEnabledCheckBox.setText(tr("Enable &sounds")); themeLabel.setText(tr("Current sounds theme:")); soundTestButton.setText(tr("Test system sound engine")); soundGroupBox->setTitle(tr("Sound settings")); - masterVolumeLabel.setText(tr("Master volume")); + masterVolumeLabel.setText(tr("Master volume")); } -DlgSettings::DlgSettings(QWidget *parent) - : QDialog(parent) +DlgSettings::DlgSettings(QWidget *parent) : QDialog(parent) { QRect rec = QApplication::desktop()->availableGeometry(); this->setMinimumSize(rec.width() / 2, rec.height() - 100); this->setBaseSize(rec.width(), rec.height()); connect(settingsCache, SIGNAL(langChanged()), this, SLOT(updateLanguage())); - + contentsWidget = new QListWidget; contentsWidget->setViewMode(QListView::IconMode); contentsWidget->setIconSize(QSize(58, 50)); @@ -778,7 +884,7 @@ DlgSettings::DlgSettings(QWidget *parent) contentsWidget->setMinimumHeight(85); contentsWidget->setMaximumHeight(85); contentsWidget->setSpacing(5); - + pagesWidget = new QStackedWidget; pagesWidget->addWidget(new GeneralSettingsPage); pagesWidget->addWidget(new AppearanceSettingsPage); @@ -787,25 +893,25 @@ DlgSettings::DlgSettings(QWidget *parent) pagesWidget->addWidget(new MessagesSettingsPage); pagesWidget->addWidget(new SoundSettingsPage); pagesWidget->addWidget(new ShortcutsTab); - + createIcons(); contentsWidget->setCurrentRow(0); - - QVBoxLayout *vboxLayout = new QVBoxLayout; + + auto *vboxLayout = new QVBoxLayout; vboxLayout->addWidget(contentsWidget); vboxLayout->addWidget(pagesWidget); - - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); + + auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); connect(buttonBox, SIGNAL(accepted()), this, SLOT(close())); - - QVBoxLayout *mainLayout = new QVBoxLayout; + + auto *mainLayout = new QVBoxLayout; mainLayout->addLayout(vboxLayout); mainLayout->addSpacing(12); mainLayout->addWidget(buttonBox); setLayout(mainLayout); - + retranslateUi(); - + adjustSize(); } @@ -815,22 +921,22 @@ void DlgSettings::createIcons() generalButton->setTextAlignment(Qt::AlignHCenter); generalButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); generalButton->setIcon(QPixmap("theme:config/general")); - + appearanceButton = new QListWidgetItem(contentsWidget); appearanceButton->setTextAlignment(Qt::AlignHCenter); appearanceButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); appearanceButton->setIcon(QPixmap("theme:config/appearance")); - + userInterfaceButton = new QListWidgetItem(contentsWidget); userInterfaceButton->setTextAlignment(Qt::AlignHCenter); userInterfaceButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); userInterfaceButton->setIcon(QPixmap("theme:config/interface")); - + deckEditorButton = new QListWidgetItem(contentsWidget); deckEditorButton->setTextAlignment(Qt::AlignHCenter); deckEditorButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); deckEditorButton->setIcon(QPixmap("theme:config/deckeditor")); - + messagesButton = new QListWidgetItem(contentsWidget); messagesButton->setTextAlignment(Qt::AlignHCenter); messagesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); @@ -840,7 +946,7 @@ void DlgSettings::createIcons() soundButton->setTextAlignment(Qt::AlignHCenter); soundButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); soundButton->setIcon(QPixmap("theme:config/sound")); - + shortcutsButton = new QListWidgetItem(contentsWidget); shortcutsButton->setTextAlignment(Qt::AlignHCenter); shortcutsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); @@ -853,11 +959,12 @@ void DlgSettings::changePage(QListWidgetItem *current, QListWidgetItem *previous { if (!current) current = previous; - + pagesWidget->setCurrentIndex(contentsWidget->row(current)); } -void DlgSettings::setTab(int index) { +void DlgSettings::setTab(int index) +{ if (index <= contentsWidget->count()-1 && index >= 0) { changePage(contentsWidget->item(index), contentsWidget->currentItem()); contentsWidget->setCurrentRow(index); @@ -883,72 +990,86 @@ void DlgSettings::closeEvent(QCloseEvent *event) QString loadErrorMessage = tr("Unknown Error loading card database"); LoadStatus loadStatus = db->getLoadStatus(); qDebug() << "Card Database load status: " << loadStatus; - switch(loadStatus) { - case Ok: - showLoadError = false; - break; - case Invalid: - loadErrorMessage = - tr("Your card database is invalid.\n\n" - "Cockatrice may not function correctly with an invalid database\n\n" - "You may need to rerun oracle to update your card database.\n\n" - "Would you like to change your database location setting?"); - break; - case VersionTooOld: - loadErrorMessage = - tr("Your card database version is too old.\n\n" - "This can cause problems loading card information or images\n\n" - "Usually this can be fixed by rerunning oracle to to update your card database.\n\n" - "Would you like to change your database location setting?"); - break; - case NotLoaded: - loadErrorMessage = - tr("Your card database did not finish loading\n\n" - "Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached\n\n" - "Would you like to change your database location setting?"); - break; - case FileError: - loadErrorMessage = - tr("File Error loading your card database.\n\n" - "Would you like to change your database location setting?"); - break; - case NoCards: - loadErrorMessage = - tr("Your card database was loaded but contains no cards.\n\n" - "Would you like to change your database location setting?"); - break; - default: - loadErrorMessage = - tr("Unknown card database load status\n\n" - "Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues\n\n" - "Would you like to change your database location setting?"); + switch(loadStatus) + { + case Ok: + showLoadError = false; + break; + case Invalid: + loadErrorMessage = + tr("Your card database is invalid.\n\n" + "Cockatrice may not function correctly with an invalid database\n\n" + "You may need to rerun oracle to update your card database.\n\n" + "Would you like to change your database location setting?"); + break; + case VersionTooOld: + loadErrorMessage = + tr("Your card database version is too old.\n\n" + "This can cause problems loading card information or images\n\n" + "Usually this can be fixed by rerunning oracle to to update your card database.\n\n" + "Would you like to change your database location setting?"); + break; + case NotLoaded: + loadErrorMessage = + tr("Your card database did not finish loading\n\n" + "Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached\n\n" + "Would you like to change your database location setting?"); + break; + case FileError: + loadErrorMessage = + tr("File Error loading your card database.\n\n" + "Would you like to change your database location setting?"); + break; + case NoCards: + loadErrorMessage = + tr("Your card database was loaded but contains no cards.\n\n" + "Would you like to change your database location setting?"); + break; + default: + loadErrorMessage = + tr("Unknown card database load status\n\n" + "Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues\n\n" + "Would you like to change your database location setting?"); - break; + break; } + if (showLoadError) - if (QMessageBox::critical(this, tr("Error"), loadErrorMessage, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + { + if (QMessageBox::critical(this, tr("Error"), loadErrorMessage, QMessageBox::Yes | QMessageBox::No) == + QMessageBox::Yes) + { event->ignore(); return; } + } + if (!QDir(settingsCache->getDeckPath()).exists() || settingsCache->getDeckPath().isEmpty()) + { // TODO: Prompt to create it - if (QMessageBox::critical(this, tr("Error"), tr("The path to your deck directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + if (QMessageBox::critical(this, tr("Error"), tr("The path to your deck directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) + { event->ignore(); return; } + } + if (!QDir(settingsCache->getPicsPath()).exists() || settingsCache->getPicsPath().isEmpty()) + { // TODO: Prompt to create it - if (QMessageBox::critical(this, tr("Error"), tr("The path to your card pictures directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + if (QMessageBox::critical(this, tr("Error"), tr("The path to your card pictures directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) + { event->ignore(); return; } + } event->accept(); } void DlgSettings::retranslateUi() { setWindowTitle(tr("Settings")); - + generalButton->setText(tr("General")); appearanceButton->setText(tr("Appearance")); userInterfaceButton->setText(tr("User Interface")); @@ -956,10 +1077,9 @@ void DlgSettings::retranslateUi() messagesButton->setText(tr("Chat")); soundButton->setText(tr("Sound")); shortcutsButton->setText(tr("Shortcuts")); - + for (int i = 0; i < pagesWidget->count(); i++) dynamic_cast(pagesWidget->widget(i))->retranslateUi(); contentsWidget->reset(); -} - +} \ No newline at end of file diff --git a/cockatrice/src/dlg_settings.h b/cockatrice/src/dlg_settings.h index 19cae38f..c437efd2 100644 --- a/cockatrice/src/dlg_settings.h +++ b/cockatrice/src/dlg_settings.h @@ -26,192 +26,227 @@ class QSpinBox; class QSlider; class QSpinBox; -class AbstractSettingsPage : public QWidget { -public: - virtual void retranslateUi() = 0; +class AbstractSettingsPage : public QWidget +{ + public: + virtual void retranslateUi() = 0; }; -class GeneralSettingsPage : public AbstractSettingsPage { +class GeneralSettingsPage : public AbstractSettingsPage +{ Q_OBJECT -public: - GeneralSettingsPage(); - void retranslateUi(); -private slots: - void deckPathButtonClicked(); - void replaysPathButtonClicked(); - void picsPathButtonClicked(); - void clearDownloadedPicsButtonClicked(); - void cardDatabasePathButtonClicked(); - void tokenDatabasePathButtonClicked(); - void languageBoxChanged(int index); - void setEnabledStatus(bool); - void defaultUrlRestoreButtonClicked(); - void fallbackUrlRestoreButtonClicked(); -private: - QStringList findQmFiles(); - QString languageName(const QString &qmFile); - QLineEdit *deckPathEdit; - QLineEdit *replaysPathEdit; - QLineEdit *picsPathEdit; - QLineEdit *cardDatabasePathEdit; - QLineEdit *tokenDatabasePathEdit; - QLineEdit *defaultUrlEdit; - QLineEdit *fallbackUrlEdit; - QSpinBox pixmapCacheEdit; - QGroupBox *personalGroupBox; - QGroupBox *pathsGroupBox; - QComboBox languageBox; - QCheckBox picDownloadCheckBox; - QCheckBox updateNotificationCheckBox; - QComboBox updateReleaseChannelBox; - QLabel languageLabel; - QLabel pixmapCacheLabel; - QLabel deckPathLabel; - QLabel replaysPathLabel; - QLabel picsPathLabel; - QLabel cardDatabasePathLabel; - QLabel tokenDatabasePathLabel; - QLabel defaultUrlLabel; - QLabel fallbackUrlLabel; - QLabel urlLinkLabel; - QLabel updateReleaseChannelLabel; - QPushButton clearDownloadedPicsButton; - QPushButton defaultUrlRestoreButton; - QPushButton fallbackUrlRestoreButton; + public: + GeneralSettingsPage(); + void retranslateUi() override; + + private slots: + void deckPathButtonClicked(); + void replaysPathButtonClicked(); + void picsPathButtonClicked(); + void clearDownloadedPicsButtonClicked(); + void cardDatabasePathButtonClicked(); + void tokenDatabasePathButtonClicked(); + void languageBoxChanged(int index); + void setEnabledStatus(bool); + void defaultUrlRestoreButtonClicked(); + void fallbackUrlRestoreButtonClicked(); + + private: + QStringList findQmFiles(); + QString languageName(const QString &qmFile); + QLineEdit *deckPathEdit; + QLineEdit *replaysPathEdit; + QLineEdit *picsPathEdit; + QLineEdit *cardDatabasePathEdit; + QLineEdit *tokenDatabasePathEdit; + QLineEdit *defaultUrlEdit; + QLineEdit *fallbackUrlEdit; + QSpinBox pixmapCacheEdit; + QGroupBox *personalGroupBox; + QGroupBox *pathsGroupBox; + QComboBox languageBox; + QCheckBox picDownloadCheckBox; + QCheckBox updateNotificationCheckBox; + QComboBox updateReleaseChannelBox; + QLabel languageLabel; + QLabel pixmapCacheLabel; + QLabel deckPathLabel; + QLabel replaysPathLabel; + QLabel picsPathLabel; + QLabel cardDatabasePathLabel; + QLabel tokenDatabasePathLabel; + QLabel defaultUrlLabel; + QLabel fallbackUrlLabel; + QLabel urlLinkLabel; + QLabel updateReleaseChannelLabel; + QPushButton clearDownloadedPicsButton; + QPushButton defaultUrlRestoreButton; + QPushButton fallbackUrlRestoreButton; }; -class AppearanceSettingsPage : public AbstractSettingsPage { +class AppearanceSettingsPage : public AbstractSettingsPage +{ Q_OBJECT -private slots: - void themeBoxChanged(int index); -private: - QLabel themeLabel; - QComboBox themeBox; - QLabel minPlayersForMultiColumnLayoutLabel; - QLabel maxFontSizeForCardsLabel; - QCheckBox displayCardNamesCheckBox; - QCheckBox cardScalingCheckBox; - QCheckBox horizontalHandCheckBox; - QCheckBox leftJustifiedHandCheckBox; - QCheckBox invertVerticalCoordinateCheckBox; - QGroupBox *themeGroupBox; - QGroupBox *cardsGroupBox; - QGroupBox *handGroupBox; - QGroupBox *tableGroupBox; - QSpinBox minPlayersForMultiColumnLayoutEdit; - QSpinBox maxFontSizeForCardsEdit; -public: - AppearanceSettingsPage(); - void retranslateUi(); + private slots: + void themeBoxChanged(int index); + + private: + QLabel themeLabel; + QComboBox themeBox; + QLabel minPlayersForMultiColumnLayoutLabel; + QLabel maxFontSizeForCardsLabel; + QCheckBox displayCardNamesCheckBox; + QCheckBox cardScalingCheckBox; + QCheckBox horizontalHandCheckBox; + QCheckBox leftJustifiedHandCheckBox; + QCheckBox invertVerticalCoordinateCheckBox; + QGroupBox *themeGroupBox; + QGroupBox *cardsGroupBox; + QGroupBox *handGroupBox; + QGroupBox *tableGroupBox; + QSpinBox minPlayersForMultiColumnLayoutEdit; + QSpinBox maxFontSizeForCardsEdit; + + public: + AppearanceSettingsPage(); + void retranslateUi() override; }; -class UserInterfaceSettingsPage : public AbstractSettingsPage { +class UserInterfaceSettingsPage : public AbstractSettingsPage +{ Q_OBJECT -private slots: - void setSpecNotificationEnabled(int); -private: - QCheckBox notificationsEnabledCheckBox; - QCheckBox specNotificationsEnabledCheckBox; - QCheckBox doubleClickToPlayCheckBox; - QCheckBox playToStackCheckBox; - QCheckBox annotateTokensCheckBox; - QCheckBox tapAnimationCheckBox; - QGroupBox *generalGroupBox; - QGroupBox *animationGroupBox; - -public: - UserInterfaceSettingsPage(); - void retranslateUi(); + private slots: + void setSpecNotificationEnabled(int); + + private: + QCheckBox notificationsEnabledCheckBox; + QCheckBox specNotificationsEnabledCheckBox; + QCheckBox doubleClickToPlayCheckBox; + QCheckBox playToStackCheckBox; + QCheckBox annotateTokensCheckBox; + QCheckBox tapAnimationCheckBox; + QGroupBox *generalGroupBox; + QGroupBox *animationGroupBox; + + public: + UserInterfaceSettingsPage(); + void retranslateUi() override; }; -class DeckEditorSettingsPage : public AbstractSettingsPage { +class DeckEditorSettingsPage : public AbstractSettingsPage +{ Q_OBJECT -public: - DeckEditorSettingsPage(); - void retranslateUi(); -private slots: -signals: -private: - QGroupBox *generalGroupBox; + public: + DeckEditorSettingsPage(); + void retranslateUi() override; + QString getLastUpdateTime(); + + private slots: + void setSpoilersEnabled(bool); + void spoilerPathButtonClicked(); + void updateSpoilers(); + void unlockSettings(); + + private: + QCheckBox mcDownloadSpoilersCheckBox; + QLabel msDownloadSpoilersLabel; + QGroupBox *mpGeneralGroupBox; + QGroupBox *mpSpoilerGroupBox; + QLineEdit *mpSpoilerSavePathLineEdit; + QLabel mcSpoilerSaveLabel; + QLabel mcGeneralMessageLabel; + QLabel infoOnSpoilersLabel; + QPushButton *mpSpoilerPathButton; + QPushButton *updateNowButton; }; -class MessagesSettingsPage : public AbstractSettingsPage { +class MessagesSettingsPage : public AbstractSettingsPage +{ Q_OBJECT -public: - MessagesSettingsPage(); - void retranslateUi(); -private slots: - void actAdd(); - void actRemove(); - void updateColor(const QString &value); - void updateHighlightColor(const QString &value); - void updateTextColor(int value); - void updateTextHighlightColor(int value); -private: - QListWidget *messageList; - QAction *aAdd; - QAction *aRemove; - QCheckBox chatMentionCheckBox; - QCheckBox chatMentionCompleterCheckbox; - QCheckBox invertMentionForeground; - QCheckBox invertHighlightForeground; - QCheckBox ignoreUnregUsersMainChat; - QCheckBox ignoreUnregUserMessages; - QCheckBox messagePopups; - QCheckBox mentionPopups; - QCheckBox roomHistory; - QGroupBox *chatGroupBox; - QGroupBox *highlightGroupBox; - QGroupBox *messageShortcuts; - QLineEdit *mentionColor; - QLineEdit *highlightColor; - QLineEdit *customAlertString; - QLabel hexLabel; - QLabel hexHighlightLabel; - QLabel customAlertStringLabel; + public: + MessagesSettingsPage(); + void retranslateUi() override; - void storeSettings(); - void updateMentionPreview(); - void updateHighlightPreview(); + private slots: + void actAdd(); + void actRemove(); + void updateColor(const QString &value); + void updateHighlightColor(const QString &value); + void updateTextColor(int value); + void updateTextHighlightColor(int value); + + private: + QListWidget *messageList; + QAction *aAdd; + QAction *aRemove; + QCheckBox chatMentionCheckBox; + QCheckBox chatMentionCompleterCheckbox; + QCheckBox invertMentionForeground; + QCheckBox invertHighlightForeground; + QCheckBox ignoreUnregUsersMainChat; + QCheckBox ignoreUnregUserMessages; + QCheckBox messagePopups; + QCheckBox mentionPopups; + QCheckBox roomHistory; + QGroupBox *chatGroupBox; + QGroupBox *highlightGroupBox; + QGroupBox *messageShortcuts; + QLineEdit *mentionColor; + QLineEdit *highlightColor; + QLineEdit *customAlertString; + QLabel hexLabel; + QLabel hexHighlightLabel; + QLabel customAlertStringLabel; + + void storeSettings(); + void updateMentionPreview(); + void updateHighlightPreview(); }; -class SoundSettingsPage : public AbstractSettingsPage { +class SoundSettingsPage : public AbstractSettingsPage +{ Q_OBJECT -public: - SoundSettingsPage(); - void retranslateUi(); -private: - QLabel themeLabel; - QComboBox themeBox; - QGroupBox *soundGroupBox; - QPushButton soundTestButton; - QCheckBox soundEnabledCheckBox; - QLabel masterVolumeLabel; - QSlider *masterVolumeSlider; - QSpinBox *masterVolumeSpinBox; -private slots: - void masterVolumeChanged(int value); - void themeBoxChanged(int index); + public: + SoundSettingsPage(); + void retranslateUi() override; + + private: + QLabel themeLabel; + QComboBox themeBox; + QGroupBox *soundGroupBox; + QPushButton soundTestButton; + QCheckBox soundEnabledCheckBox; + QLabel masterVolumeLabel; + QSlider *masterVolumeSlider; + QSpinBox *masterVolumeSpinBox; + + private slots: + void masterVolumeChanged(int value); + void themeBoxChanged(int index); }; -class DlgSettings : public QDialog { +class DlgSettings : public QDialog +{ Q_OBJECT -public: - DlgSettings(QWidget *parent = 0); - void setTab(int index); -private slots: - void changePage(QListWidgetItem *current, QListWidgetItem *previous); - void updateLanguage(); -private: - QListWidget *contentsWidget; - QStackedWidget *pagesWidget; - QListWidgetItem *generalButton, *appearanceButton, *userInterfaceButton, *deckEditorButton, *messagesButton, *soundButton; - QListWidgetItem *shortcutsButton; - void createIcons(); - void retranslateUi(); -protected: - void changeEvent(QEvent *event); - void closeEvent(QCloseEvent *event); + public: + explicit DlgSettings(QWidget *parent = nullptr); + void setTab(int index); + + private slots: + void changePage(QListWidgetItem *current, QListWidgetItem *previous); + void updateLanguage(); + + private: + QListWidget *contentsWidget; + QStackedWidget *pagesWidget; + QListWidgetItem *generalButton, *appearanceButton, *userInterfaceButton, *deckEditorButton, *messagesButton, *soundButton; + QListWidgetItem *shortcutsButton; + void createIcons(); + void retranslateUi(); + + protected: + void changeEvent(QEvent *event) override; + void closeEvent(QCloseEvent *event) override; }; -#endif +#endif \ No newline at end of file diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index 0678b986..529fb5e2 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -42,6 +42,7 @@ #include "soundengine.h" #include "featureset.h" #include "logger.h" +#include "spoilerbackgroundupdater.h" CardDatabase *db; QTranslator *translator, *qtTranslator; @@ -129,6 +130,10 @@ int main(int argc, char *argv[]) settingsCache->setClientID(generateClientID()); + // If spoiler mode is enabled, we will download the spoilers + // then reload the DB. otherwise just reload the DB + SpoilerBackgroundUpdater spoilerBackgroundUpdater; + ui.show(); qDebug("main(): ui.show() finished"); diff --git a/cockatrice/src/main.h b/cockatrice/src/main.h index 0326040f..e40d9f7b 100644 --- a/cockatrice/src/main.h +++ b/cockatrice/src/main.h @@ -7,6 +7,7 @@ class QSystemTrayIcon; class SoundEngine; extern CardDatabase *db; + extern QSystemTrayIcon *trayIcon; extern QTranslator *translator; extern const QString translationPrefix; diff --git a/cockatrice/src/settings/carddatabasesettings.cpp b/cockatrice/src/settings/carddatabasesettings.cpp index 39b23bfc..4218438a 100644 --- a/cockatrice/src/settings/carddatabasesettings.cpp +++ b/cockatrice/src/settings/carddatabasesettings.cpp @@ -1,36 +1,36 @@ #include "carddatabasesettings.h" -CardDatabaseSettings::CardDatabaseSettings(QString settingPath, QObject *parent) - : SettingsManager(settingPath+"cardDatabase.ini", parent) +CardDatabaseSettings::CardDatabaseSettings(QString settingPath, QObject *parent) : SettingsManager(settingPath+"cardDatabase.ini", parent) { + } void CardDatabaseSettings::setSortKey(QString shortName, unsigned int sortKey) { - setValue(sortKey,"sortkey", "sets", shortName); + setValue(sortKey, "sortkey", "sets", std::move(shortName)); } void CardDatabaseSettings::setEnabled(QString shortName, bool enabled) { - setValue(enabled, "enabled", "sets", shortName); + setValue(enabled, "enabled", "sets", std::move(shortName)); } void CardDatabaseSettings::setIsKnown(QString shortName, bool isknown) { - setValue(isknown, "isknown", "sets", shortName); + setValue(isknown, "isknown", "sets", std::move(shortName)); } unsigned int CardDatabaseSettings::getSortKey(QString shortName) { - return getValue("sortkey", "sets", shortName).toUInt(); + return getValue("sortkey", "sets", std::move(shortName)).toUInt(); } bool CardDatabaseSettings::isEnabled(QString shortName) { - return getValue("enabled", "sets", shortName).toBool(); + return getValue("enabled", "sets", std::move(shortName)).toBool(); } bool CardDatabaseSettings::isKnown(QString shortName) { - return getValue("isknown", "sets", shortName).toBool(); + return getValue("isknown", "sets", std::move(shortName)).toBool(); } diff --git a/cockatrice/src/settings/settingsmanager.cpp b/cockatrice/src/settings/settingsmanager.cpp index 0735fd41..fad4119a 100644 --- a/cockatrice/src/settings/settingsmanager.cpp +++ b/cockatrice/src/settings/settingsmanager.cpp @@ -1,43 +1,58 @@ #include "settingsmanager.h" -SettingsManager::SettingsManager(QString settingPath, QObject *parent) - : QObject(parent), - settings(settingPath, QSettings::IniFormat) +SettingsManager::SettingsManager(QString settingPath, QObject *parent) : QObject(parent), settings(settingPath, QSettings::IniFormat) { + } void SettingsManager::setValue(QVariant value, QString name, QString group, QString subGroup) { - if(!group.isEmpty()) + if (!group.isEmpty()) + { settings.beginGroup(group); + } - if(!subGroup.isEmpty()) + if (!subGroup.isEmpty()) + { settings.beginGroup(subGroup); + } settings.setValue(name, value); - if(!subGroup.isEmpty()) + if (!subGroup.isEmpty()) + { settings.endGroup(); + } - if(!group.isEmpty()) + if (!group.isEmpty()) + { settings.endGroup(); + } } QVariant SettingsManager::getValue(QString name, QString group, QString subGroup) { - if(!group.isEmpty()) + if (!group.isEmpty()) + { settings.beginGroup(group); + } - if(!subGroup.isEmpty()) + if (!subGroup.isEmpty()) + { settings.beginGroup(subGroup); + } QVariant value = settings.value(name); - if(!subGroup.isEmpty()) + if (!subGroup.isEmpty()) + { settings.endGroup(); + } - if(!group.isEmpty()) + if (!group.isEmpty()) + { settings.endGroup(); + } return value; } diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index ffbc8f4f..d78d9b1d 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -171,6 +171,8 @@ SettingsCache::SettingsCache() releaseChannels << new StableReleaseChannel(); releaseChannels << new DevReleaseChannel(); + mbDownloadSpoilers = settings->value("personal/downloadspoilers", false).toBool(); + notifyAboutUpdates = settings->value("personal/updatenotification", true).toBool(); updateReleaseChannel = settings->value("personal/updatereleasechannel", 0).toInt(); @@ -187,6 +189,7 @@ SettingsCache::SettingsCache() cardDatabasePath = getSafeConfigFilePath("paths/carddatabase", dataPath + "/cards.xml"); tokenDatabasePath = getSafeConfigFilePath("paths/tokendatabase", dataPath + "/tokens.xml"); + spoilerDatabasePath = getSafeConfigFilePath("paths/spoilerdatabase", dataPath + "/spoiler.xml"); themeName = settings->value("theme/name").toString(); @@ -348,6 +351,13 @@ void SettingsCache::setCardDatabasePath(const QString &_cardDatabasePath) emit cardDatabasePathChanged(); } +void SettingsCache::setSpoilerDatabasePath(const QString &_spoilerDatabasePath) +{ + spoilerDatabasePath = _spoilerDatabasePath; + settings->setValue("paths/spoilerdatabase", spoilerDatabasePath); + emit cardDatabasePathChanged(); +} + void SettingsCache::setTokenDatabasePath(const QString &_tokenDatabasePath) { tokenDatabasePath = _tokenDatabasePath; @@ -644,10 +654,17 @@ void SettingsCache::setRememberGameSettings(const bool _rememberGameSettings) void SettingsCache::setNotifyAboutUpdate(int _notifyaboutupdate) { - notifyAboutUpdates = _notifyaboutupdate; + notifyAboutUpdates = static_cast(_notifyaboutupdate); settings->setValue("personal/updatenotification", notifyAboutUpdates); } +void SettingsCache::setDownloadSpoilerStatus(bool _spoilerStatus) +{ + mbDownloadSpoilers = _spoilerStatus; + settings->setValue("personal/downloadspoilers", mbDownloadSpoilers); + emit downloadSpoilerStatusChanged(); +} + void SettingsCache::setUpdateReleaseChannel(int _updateReleaseChannel) { updateReleaseChannel = _updateReleaseChannel; diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index 4969097b..b4320587 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -48,6 +48,8 @@ signals: void pixmapCacheSizeChanged(int newSizeInMBs); void masterVolumeChanged(int value); void chatMentionCompleterChanged(); + void downloadSpoilerTimeIndexChanged(); + void downloadSpoilerStatusChanged(); private: QSettings *settings; ShortcutsSettings *shortcutsSettings; @@ -60,8 +62,9 @@ private: QByteArray mainWindowGeometry; QByteArray tokenDialogGeometry; QString lang; - QString deckPath, replaysPath, picsPath, customPicsPath, cardDatabasePath, customCardDatabasePath, tokenDatabasePath, themeName; + QString deckPath, replaysPath, picsPath, customPicsPath, cardDatabasePath, customCardDatabasePath, spoilerDatabasePath, tokenDatabasePath, themeName; bool notifyAboutUpdates; + bool mbDownloadSpoilers; int updateReleaseChannel; int maxFontSize; bool picDownload; @@ -130,6 +133,7 @@ public: QString getCustomPicsPath() const { return customPicsPath; } QString getCustomCardDatabasePath() const { return customCardDatabasePath; } QString getCardDatabasePath() const { return cardDatabasePath; } + QString getSpoilerCardDatabasePath() const { return spoilerDatabasePath; } QString getTokenDatabasePath() const { return tokenDatabasePath; } QString getThemeName() const { return themeName; } QString getChatMentionColor() const { return chatMentionColor; } @@ -200,7 +204,10 @@ public: GameFiltersSettings& gameFilters() const { return *gameFiltersSettings; } LayoutsSettings& layouts() const { return *layoutsSettings; } bool getIsPortableBuild() const { return isPortableBuild; } + bool getDownloadSpoilersStatus() const { return mbDownloadSpoilers; } public slots: + void setDownloadSpoilerStatus(bool _spoilerStatus); + void setMainWindowGeometry(const QByteArray &_mainWindowGeometry); void setTokenDialogGeometry(const QByteArray &_tokenDialog); void setLang(const QString &_lang); @@ -208,6 +215,7 @@ public slots: void setReplaysPath(const QString &_replaysPath); void setPicsPath(const QString &_picsPath); void setCardDatabasePath(const QString &_cardDatabasePath); + void setSpoilerDatabasePath(const QString &_spoilerDatabasePath); void setTokenDatabasePath(const QString &_tokenDatabasePath); void setThemeName(const QString &_themeName); void setChatMentionColor(const QString &_chatMentionColor); diff --git a/cockatrice/src/spoilerbackgroundupdater.cpp b/cockatrice/src/spoilerbackgroundupdater.cpp new file mode 100644 index 00000000..b0af5b32 --- /dev/null +++ b/cockatrice/src/spoilerbackgroundupdater.cpp @@ -0,0 +1,251 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spoilerbackgroundupdater.h" +#include "settingscache.h" +#include "carddatabase.h" +#include "main.h" +#include "window_main.h" + +#define SPOILERS_STATUS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/SpoilerSeasonEnabled" +#define SPOILERS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/spoiler.xml" + +SpoilerBackgroundUpdater::SpoilerBackgroundUpdater(QObject *apParent) : QObject(apParent), cardUpdateProcess(nullptr) +{ + isSpoilerDownloadEnabled = settingsCache->getDownloadSpoilersStatus(); + if (isSpoilerDownloadEnabled) + { + // Start the process of checking if we're in spoiler season + // File exists means we're in spoiler season + // We will load the database before attempting to download spoilers, incase they fail + QtConcurrent::run(db, &CardDatabase::loadCardDatabases); + startSpoilerDownloadProcess(SPOILERS_STATUS_URL, false); + } +} + +void SpoilerBackgroundUpdater::startSpoilerDownloadProcess(QString url, bool saveResults) +{ + auto spoilerURL = QUrl(url); + downloadFromURL(spoilerURL, saveResults); +} + +void SpoilerBackgroundUpdater::downloadFromURL(QUrl url, bool saveResults) +{ + auto *nam = new QNetworkAccessManager(this); + QNetworkReply *reply = nam->get(QNetworkRequest(url)); + + if (saveResults) + { + // This will write out to the file (used for spoiler.xml) + connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSpoilersFile())); + } + else + { + // This will check the status (used to see if we're in spoiler season or not) + connect(reply, SIGNAL(finished()), this, SLOT(actCheckIfSpoilerSeasonEnabled())); + } +} + +void SpoilerBackgroundUpdater::actDownloadFinishedSpoilersFile() +{ + // Check for server reply + auto *reply = dynamic_cast(sender()); + QNetworkReply::NetworkError errorCode = reply->error(); + + if (errorCode == QNetworkReply::NoError) + { + spoilerData = reply->readAll(); + + // Save the spoiler.xml file to the disk + saveDownloadedFile(spoilerData); + + reply->deleteLater(); + emit spoilerCheckerDone(); + } + else + { + qDebug() << "Error downloading spoilers file" << errorCode; + emit spoilerCheckerDone(); + } +} + +bool SpoilerBackgroundUpdater::deleteSpoilerFile() +{ + QString fileName = settingsCache->getSpoilerCardDatabasePath(); + QFileInfo fi(fileName); + QDir fileDir(fi.path()); + QFile file(fileName); + + // Delete the spoiler.xml file + if (file.exists() && file.remove()) + { + qDebug() << "Deleting spoiler.xml"; + return true; + + } + + qDebug() << "Error: Spoiler.xml not found or not deleted"; + return false; +} + +void SpoilerBackgroundUpdater::actCheckIfSpoilerSeasonEnabled() +{ + auto *response = dynamic_cast(sender()); + QNetworkReply::NetworkError errorCode = response->error(); + + if (errorCode == QNetworkReply::ContentNotFoundError) + { + // Spoiler season is offline at this point, so the spoiler.xml file can be safely deleted + // The user should run Oracle to get the latest card information + if (deleteSpoilerFile() && trayIcon) + { + trayIcon->showMessage(tr("Spoilers season has ended"), tr("Deleting spoiler.xml. Please run Oracle")); + } + + qDebug() << "Spoiler Season Offline"; + emit spoilerCheckerDone(); + } + else if (errorCode == QNetworkReply::NoError) + { + qDebug() << "Spoiler Service Online"; + startSpoilerDownloadProcess(SPOILERS_URL, true); + } + else if (errorCode == QNetworkReply::HostNotFoundError) + { + if (trayIcon) + { + trayIcon->showMessage(tr("Spoilers download failed"), tr("No internet connection")); + } + + qDebug() << "Spoiler download failed due to no internet connection"; + emit spoilerCheckerDone(); + } + else + { + if (trayIcon) + { + trayIcon->showMessage(tr("Spoilers download failed"), tr("Error") + " " + errorCode); + } + + qDebug() << "Spoiler download failed with reason" << errorCode; + emit spoilerCheckerDone(); + } +} + +bool SpoilerBackgroundUpdater::saveDownloadedFile(QByteArray data) +{ + QString fileName = settingsCache->getSpoilerCardDatabasePath(); + QFileInfo fi(fileName); + QDir fileDir(fi.path()); + + if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) + { + return false; + } + + // Check if the data matches. If it does, then spoilers are up to date. + if (getHash(fileName) == getHash(data)) + { + if (trayIcon) + { + trayIcon->showMessage(tr("Spoilers already up to date"), tr("No new spoilers added")); + } + + qDebug() << "Spoilers Up to Date"; + return false; + } + + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly)) + { + qDebug() << "Spoiler Service Error: File open (w) failed for" << fileName; + file.close(); + return false; + } + + if (file.write(data) == -1) + { + qDebug() << "Spoiler Service Error: File write (w) failed for" << fileName; + file.close(); + return false; + } + + file.close(); + + + // Data written, so reload the card database + qDebug() << "Spoiler Service Data Written"; + QtConcurrent::run(db, &CardDatabase::loadCardDatabases); + + // If the user has notifications enabled, let them know + // when the database was last updated + if (trayIcon) + { + QList lines = data.split('\n'); + + foreach (QByteArray line, lines) + { + if (line.indexOf("created:") > -1) + { + QString timeStamp = QString(line).replace("created:", "").trimmed(); + timeStamp.chop(6); // Remove " (UTC)" + + auto utcTime = QDateTime::fromString(timeStamp, QString("ddd, MMM dd yyyy, hh:mm:ss")); + utcTime.setTimeSpec(Qt::UTC); + + QString localTime = utcTime.toLocalTime().toString("MMM d, hh:mm"); + + trayIcon->showMessage(tr("Spoilers have been updated!"), tr("Last change:") + " " + localTime); + emit spoilersUpdatedSuccessfully(); + return true; + } + } + } + + return true; +} + +QByteArray SpoilerBackgroundUpdater::getHash(const QString fileName) +{ + QFile file(fileName); + + if (file.open(QFile::ReadOnly)) + { + // Only read the first 512 bytes (enough to get the "created" tag) + const QByteArray bytes = file.read(512); + + QCryptographicHash hash(QCryptographicHash::Algorithm::Md5); + hash.addData(bytes); + + qDebug() << "File Hash =" << hash.result(); + + file.close(); + return hash.result(); + } + else + { + qDebug() << "getHash ReadOnly failed!"; + file.close(); + return QByteArray(); + } +} + +QByteArray SpoilerBackgroundUpdater::getHash(QByteArray data) +{ + // Only read the first 512 bytes (enough to get the "created" tag) + const QByteArray bytes = data.left(512); + + QCryptographicHash hash(QCryptographicHash::Algorithm::Md5); + hash.addData(bytes); + + qDebug() << "Data Hash =" << hash.result(); + + return hash.result(); +} diff --git a/cockatrice/src/spoilerbackgroundupdater.h b/cockatrice/src/spoilerbackgroundupdater.h new file mode 100644 index 00000000..8e5a10f7 --- /dev/null +++ b/cockatrice/src/spoilerbackgroundupdater.h @@ -0,0 +1,35 @@ +#ifndef COCKATRICE_SPOILER_DOWNLOADER_H +#define COCKATRICE_SPOILER_DOWNLOADER_H + +#include +#include +#include + +class SpoilerBackgroundUpdater : public QObject +{ + Q_OBJECT + public: + explicit SpoilerBackgroundUpdater(QObject *apParent = nullptr); + inline QString getCardUpdaterBinaryName() { return "oracle"; }; + QByteArray getHash(const QString fileName); + QByteArray getHash(QByteArray data); + static bool deleteSpoilerFile(); + + private slots: + void actDownloadFinishedSpoilersFile(); + void actCheckIfSpoilerSeasonEnabled(); + + private: + bool isSpoilerDownloadEnabled; + QProcess *cardUpdateProcess; + QByteArray spoilerData; + void startSpoilerDownloadProcess(QString url, bool saveResults); + void downloadFromURL(QUrl url, bool saveResults); + bool saveDownloadedFile(QByteArray data); + + signals: + void spoilersUpdatedSuccessfully(); + void spoilerCheckerDone(); +}; + +#endif //COCKATRICE_SPOILER_DOWNLOADER_H \ No newline at end of file diff --git a/cockatrice/src/window_main.cpp b/cockatrice/src/window_main.cpp index f52f142c..3d81472a 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -76,11 +76,11 @@ const QStringList MainWindow::fileNameFilters = QStringList() void MainWindow::updateTabMenu(const QList &newMenuList) { - for (int i = 0; i < tabMenus.size(); ++i) - menuBar()->removeAction(tabMenus[i]->menuAction()); + for (auto &tabMenu : tabMenus) + menuBar()->removeAction(tabMenu->menuAction()); tabMenus = newMenuList; - for (int i = 0; i < tabMenus.size(); ++i) - menuBar()->insertMenu(helpMenu->menuAction(), tabMenus[i]); + for (auto &tabMenu : tabMenus) + menuBar()->insertMenu(helpMenu->menuAction(), tabMenu); } void MainWindow::processConnectionClosedEvent(const Event_ConnectionClosed &event) @@ -166,10 +166,10 @@ void MainWindow::activateAccepted() void MainWindow::actConnect() { - DlgConnect *dlg = new DlgConnect(this); + auto *dlg = new DlgConnect(this); connect(dlg, SIGNAL(sigStartForgotPasswordRequest()), this, SLOT(actForgotPasswordRequest())); if (dlg->exec()) - client->connectToServer(dlg->getHost(), dlg->getPort(), dlg->getPlayerName(), dlg->getPassword()); + client->connectToServer(dlg->getHost(), static_cast(dlg->getPort()), dlg->getPlayerName(), dlg->getPassword()); } void MainWindow::actRegister() @@ -179,7 +179,7 @@ void MainWindow::actRegister() { client->registerToServer( dlg.getHost(), - dlg.getPort(), + static_cast(dlg.getPort()), dlg.getPlayerName(), dlg.getPassword(), dlg.getEmail(), @@ -220,7 +220,7 @@ void MainWindow::actSinglePlayer() tabSupervisor->startLocal(localClients); Command_CreateGame createCommand; - createCommand.set_max_players(numberPlayers); + createCommand.set_max_players(static_cast(numberPlayers)); mainClient->sendCommand(mainClient->prepareRoomCommand(createCommand, 0)); } @@ -239,7 +239,7 @@ void MainWindow::actWatchReplay() QByteArray buf = file.readAll(); file.close(); - GameReplay *replay = new GameReplay; + auto *replay = new GameReplay; replay->ParseFromArray(buf.data(), buf.size()); tabSupervisor->openReplay(replay); @@ -248,7 +248,7 @@ void MainWindow::actWatchReplay() void MainWindow::localGameEnded() { delete localServer; - localServer = 0; + localServer = nullptr; aConnect->setEnabled(true); aRegister->setEnabled(true); @@ -257,7 +257,7 @@ void MainWindow::localGameEnded() void MainWindow::actDeckEditor() { - tabSupervisor->addDeckEditorTab(0); + tabSupervisor->addDeckEditorTab(nullptr); } void MainWindow::actFullScreen(bool checked) @@ -655,7 +655,7 @@ void MainWindow::createMenus() } MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent), localServer(0), bHasActivated(false), cardUpdateProcess(0), logviewDialog(0) + : QMainWindow(parent), localServer(nullptr), bHasActivated(false), cardUpdateProcess(nullptr), logviewDialog(nullptr) { connect(settingsCache, SIGNAL(pixmapCacheSizeChanged(int)), this, SLOT(pixmapCacheSizeChanged(int))); pixmapCacheSizeChanged(settingsCache->getPixmapCacheSize()); @@ -691,7 +691,7 @@ MainWindow::MainWindow(QWidget *parent) connect(tabSupervisor, SIGNAL(setMenu(QList)), this, SLOT(updateTabMenu(QList))); connect(tabSupervisor, SIGNAL(localGameEnded()), this, SLOT(localGameEnded())); connect(tabSupervisor, SIGNAL(showWindowIfHidden()), this, SLOT(showWindowIfHidden())); - tabSupervisor->addDeckEditorTab(0); + tabSupervisor->addDeckEditorTab(nullptr); setCentralWidget(tabSupervisor); @@ -699,7 +699,7 @@ MainWindow::MainWindow(QWidget *parent) resize(900, 700); restoreGeometry(settingsCache->getMainWindowGeometry()); - aFullScreen->setChecked(windowState() & Qt::WindowFullScreen); + aFullScreen->setChecked(static_cast(windowState() & Qt::WindowFullScreen)); if (QSystemTrayIcon::isSystemTrayAvailable()) { createTrayActions(); @@ -712,7 +712,12 @@ MainWindow::MainWindow(QWidget *parent) connect(db, SIGNAL(cardDatabaseLoadingFailed()), this, SLOT(cardDatabaseLoadingFailed())); connect(db, SIGNAL(cardDatabaseNewSetsFound(int, QStringList)), this, SLOT(cardDatabaseNewSetsFound(int, QStringList))); connect(db, SIGNAL(cardDatabaseAllNewSetsEnabled()), this, SLOT(cardDatabaseAllNewSetsEnabled())); - QtConcurrent::run(db, &CardDatabase::loadCardDatabases); + + if (! settingsCache->getDownloadSpoilersStatus()) + { + qDebug() << "Spoilers Disabled"; + QtConcurrent::run(db, &CardDatabase::loadCardDatabases); + } } MainWindow::~MainWindow() @@ -727,7 +732,7 @@ MainWindow::~MainWindow() } void MainWindow::createTrayIcon() { - QMenu *trayIconMenu = new QMenu(this); + auto *trayIconMenu = new QMenu(this); trayIconMenu->addAction(closeAction); trayIcon = new QSystemTrayIcon(this); @@ -754,7 +759,7 @@ void MainWindow::promptForgotPasswordChallenge() { DlgForgotPasswordChallenge dlg(this); if (dlg.exec()) - client->submitForgotPasswordChallengeToServer(dlg.getHost(),dlg.getPort(),dlg.getPlayerName(),dlg.getEmail()); + client->submitForgotPasswordChallengeToServer(dlg.getHost(), static_cast(dlg.getPort()), dlg.getPlayerName(), dlg.getEmail()); } @@ -794,7 +799,7 @@ void MainWindow::changeEvent(QEvent *event) if(settingsCache->servers().getAutoConnect()) { qDebug() << "Attempting auto-connect..."; DlgConnect dlg(this); - client->connectToServer(dlg.getHost(), dlg.getPort(), dlg.getPlayerName(), dlg.getPassword()); + client->connectToServer(dlg.getHost(), static_cast(dlg.getPort()), dlg.getPlayerName(), dlg.getPassword()); } } } @@ -856,11 +861,17 @@ void MainWindow::cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknow msgBox.exec(); - if (msgBox.clickedButton() == yesButton) { + if (msgBox.clickedButton() == yesButton) + { db->enableAllUnknownSets(); - } else if (msgBox.clickedButton() == noButton) { + QtConcurrent::run(db, &CardDatabase::loadCardDatabases); + } + else if (msgBox.clickedButton() == noButton) + { db->markAllSetsAsKnown(); - } else if (msgBox.clickedButton() == settingsButton) { + } + else if (msgBox.clickedButton() == settingsButton) + { db->markAllSetsAsKnown(); actEditSets(); } @@ -873,10 +884,9 @@ void MainWindow::cardDatabaseAllNewSetsEnabled() } /* CARD UPDATER */ - void MainWindow::actCheckCardUpdates() { - if(cardUpdateProcess) + if (cardUpdateProcess) { QMessageBox::information(this, tr("Information"), tr("A card database update is already running.")); return; @@ -946,7 +956,7 @@ void MainWindow::cardUpdateError(QProcess::ProcessError err) } cardUpdateProcess->deleteLater(); - cardUpdateProcess = 0; + cardUpdateProcess = nullptr; QMessageBox::warning(this, tr("Error"), tr("The card database updater exited with an error: %1").arg(error)); } @@ -954,10 +964,9 @@ void MainWindow::cardUpdateError(QProcess::ProcessError err) void MainWindow::cardUpdateFinished(int, QProcess::ExitStatus) { cardUpdateProcess->deleteLater(); - cardUpdateProcess = 0; + cardUpdateProcess = nullptr; QMessageBox::information(this, tr("Information"), tr("Update completed successfully.\nCockatrice will now reload the card database.")); - QtConcurrent::run(db, &CardDatabase::loadCardDatabases); } @@ -989,7 +998,7 @@ void MainWindow::actOpenCustomFolder() #if defined(Q_OS_MAC) QStringList scriptArgs; scriptArgs << QLatin1String("-e"); - scriptArgs << QString::fromLatin1("tell application \"Finder\" to open POSIX file \"%1\"").arg(dir); + scriptArgs << QString::fromLatin1(R"(tell application "Finder" to open POSIX file "%1")").arg(dir); scriptArgs << QLatin1String("-e"); scriptArgs << QLatin1String("tell application \"Finder\" to activate"); @@ -1008,7 +1017,7 @@ void MainWindow::actOpenCustomsetsFolder() #if defined(Q_OS_MAC) QStringList scriptArgs; scriptArgs << QLatin1String("-e"); - scriptArgs << QString::fromLatin1("tell application \"Finder\" to open POSIX file \"%1\"").arg(dir); + scriptArgs << QString::fromLatin1(R"(tell application "Finder" to open POSIX file "%1")").arg(dir); scriptArgs << QLatin1String("-e"); scriptArgs << QLatin1String("tell application \"Finder\" to activate"); @@ -1025,16 +1034,20 @@ void MainWindow::actAddCustomSet() QFileDialog dialog(this, tr("Load sets/cards"), QDir::homePath()); dialog.setNameFilters(MainWindow::fileNameFilters); if (!dialog.exec()) + { return; + } QString fullFilePath = dialog.selectedFiles().at(0); - if (!QFile::exists(fullFilePath)) { + if (!QFile::exists(fullFilePath)) + { QMessageBox::warning(this, tr("Load sets/cards"), tr("Selected file cannot be found.")); return; } - if (QFileInfo(fullFilePath).suffix() != "xml") { // fileName = *.xml + if (QFileInfo(fullFilePath).suffix() != "xml") // fileName = *.xml + { QMessageBox::warning(this, tr("Load sets/cards"), tr("You can only import XML databases at this time.")); return; } @@ -1042,7 +1055,7 @@ void MainWindow::actAddCustomSet() QDir dir = settingsCache->getCustomCardDatabasePath(); int nextPrefix = getNextCustomSetPrefix(dir); - bool res = false; + bool res; QString fileName = QFileInfo(fullFilePath).fileName(); if (fileName.compare("spoiler.xml", Qt::CaseInsensitive) == 0) @@ -1078,7 +1091,8 @@ void MainWindow::actAddCustomSet() } } -int MainWindow::getNextCustomSetPrefix(QDir dataDir) { +int MainWindow::getNextCustomSetPrefix(QDir dataDir) +{ QStringList files = dataDir.entryList(); int maxIndex = 0; @@ -1094,7 +1108,7 @@ int MainWindow::getNextCustomSetPrefix(QDir dataDir) { void MainWindow::actEditSets() { - WndSets *w = new WndSets; + auto *w = new WndSets; w->setWindowModality(Qt::WindowModal); w->show(); } @@ -1110,7 +1124,7 @@ void MainWindow::actForgotPasswordRequest() { DlgForgotPasswordRequest dlg(this); if (dlg.exec()) - client->requestForgotPasswordToServer(dlg.getHost(), dlg.getPort(), dlg.getPlayerName()); + client->requestForgotPasswordToServer(dlg.getHost(), static_cast(dlg.getPort()), dlg.getPlayerName()); } void MainWindow::forgotPasswordSuccess() @@ -1134,5 +1148,8 @@ void MainWindow::promptForgotPasswordReset() QMessageBox::information(this, tr("Forgot Password"), tr("Activation request received, please check your email for an activation token.")); DlgForgotPasswordReset dlg(this); if (dlg.exec()) - client->submitForgotPasswordResetToServer(dlg.getHost(), dlg.getPort(), dlg.getPlayerName(), dlg.getToken(), dlg.getPassword()); + { + client->submitForgotPasswordResetToServer(dlg.getHost(), static_cast(dlg.getPort()), + dlg.getPlayerName(), dlg.getToken(), dlg.getPassword()); + } } diff --git a/cockatrice/src/window_main.h b/cockatrice/src/window_main.h index 15c40be8..08765a39 100644 --- a/cockatrice/src/window_main.h +++ b/cockatrice/src/window_main.h @@ -40,6 +40,8 @@ class DlgViewLog; class MainWindow : public QMainWindow { Q_OBJECT +public slots: + void actCheckCardUpdates(); private slots: void updateTabMenu(const QList &newMenuList); void statusChanged(ClientStatus _status); @@ -78,7 +80,6 @@ private slots: void promptForgotPasswordChallenge(); void showWindowIfHidden(); - void actCheckCardUpdates(); void cardUpdateError(QProcess::ProcessError err); void cardUpdateFinished(int exitCode, QProcess::ExitStatus exitStatus); void refreshShortcuts(); diff --git a/oracle/src/main.cpp b/oracle/src/main.cpp index 8d272328..697f8cb3 100644 --- a/oracle/src/main.cpp +++ b/oracle/src/main.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "main.h" #include "oraclewizard.h" @@ -15,6 +16,7 @@ ThemeManager *themeManager; const QString translationPrefix = "oracle"; QString translationPath; +bool isSpoilersOnly; void installNewTranslator() { @@ -35,6 +37,13 @@ int main(int argc, char *argv[]) // this can't be changed, as it influences the default savepath for cards.xml QCoreApplication::setApplicationName("Cockatrice"); + // If the program is opened with the -s flag, it will only do spoilers. Otherwise it will do MTGJSON/Tokens + QCommandLineParser parser; + QCommandLineOption showProgressOption("s", QCoreApplication::translate("main", "Only run in spoiler mode")); + parser.addOption(showProgressOption); + parser.process(app); + isSpoilersOnly = parser.isSet(showProgressOption); + #ifdef Q_OS_MAC translationPath = qApp->applicationDirPath() + "/../Resources/translations"; #elif defined(Q_OS_WIN) diff --git a/oracle/src/main.h b/oracle/src/main.h index e4b063eb..60d9661b 100644 --- a/oracle/src/main.h +++ b/oracle/src/main.h @@ -6,6 +6,7 @@ class QTranslator; extern QTranslator *translator; extern const QString translationPrefix; extern QString translationPath; +extern bool isSpoilersOnly; void installNewTranslator(); diff --git a/oracle/src/oraclewizard.cpp b/oracle/src/oraclewizard.cpp index 99fd26ff..f6d66746 100644 --- a/oracle/src/oraclewizard.cpp +++ b/oracle/src/oraclewizard.cpp @@ -37,21 +37,28 @@ #endif #define TOKENS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml" +#define SPOILERS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/spoiler.xml" - -OracleWizard::OracleWizard(QWidget *parent) - : QWizard(parent) +OracleWizard::OracleWizard(QWidget *parent) : QWizard(parent) { settings = new QSettings(settingsCache->getSettingsPath()+"global.ini",QSettings::IniFormat, this); connect(settingsCache, SIGNAL(langChanged()), this, SLOT(updateLanguage())); importer = new OracleImporter(settingsCache->getDataPath(), this); - addPage(new IntroPage); - addPage(new LoadSetsPage); - addPage(new SaveSetsPage); - addPage(new LoadTokensPage); - addPage(new SaveTokensPage); + if (! isSpoilersOnly) + { + addPage(new IntroPage); + addPage(new LoadSetsPage); + addPage(new SaveSetsPage); + addPage(new LoadTokensPage); + addPage(new SaveTokensPage); + } + else + { + addPage(new LoadSpoilersPage); + addPage(new SaveSpoilersPage); + } retranslateUi(); } @@ -65,7 +72,10 @@ void OracleWizard::updateLanguage() void OracleWizard::changeEvent(QEvent *event) { if (event->type() == QEvent::LanguageChange) + { retranslateUi(); + } + QDialog::changeEvent(event); } @@ -75,7 +85,9 @@ void OracleWizard::retranslateUi() QWizard::setButtonText(QWizard::FinishButton, tr("Save")); for (int i = 0; i < pageIds().count(); i++) + { dynamic_cast(page(i))->retranslateUi(); + } } void OracleWizard::accept() @@ -98,22 +110,23 @@ void OracleWizard::disableButtons() bool OracleWizard::saveTokensToFile(const QString & fileName) { QFile file(fileName); - if(!file.open(QIODevice::WriteOnly)) + if (!file.open(QIODevice::WriteOnly)) { qDebug() << "File open (w) failed for" << fileName; return false; } - if(file.write(tokensData) == -1) + + if (file.write(tokensData) == -1) { qDebug() << "File write (w) failed for" << fileName; return false; } + file.close(); return true; } -IntroPage::IntroPage(QWidget *parent) - : OracleWizardPage(parent) +IntroPage::IntroPage(QWidget *parent) : OracleWizardPage(parent) { label = new QLabel(this); label->setWordWrap(true); @@ -122,16 +135,21 @@ IntroPage::IntroPage(QWidget *parent) versionLabel = new QLabel(this); languageBox = new QComboBox(this); QString setLanguage = settingsCache->getLang(); + QStringList qmFiles = findQmFiles(); - for (int i = 0; i < qmFiles.size(); i++) { + for (int i = 0; i < qmFiles.size(); i++) + { QString langName = languageName(qmFiles[i]); languageBox->addItem(langName, qmFiles[i]); if ((qmFiles[i] == setLanguage) || (setLanguage.isEmpty() && langName == QCoreApplication::translate("i18n", DEFAULT_LANG_NAME))) + { languageBox->setCurrentIndex(i); + } } + connect(languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int))); - QGridLayout *layout = new QGridLayout(this); + auto *layout = new QGridLayout(this); layout->addWidget(label, 0, 0, 1, 2); layout->addWidget(languageLabel, 1, 0); layout->addWidget(languageBox, 1, 1); @@ -150,8 +168,10 @@ QStringList IntroPage::findQmFiles() QString IntroPage::languageName(const QString &qmFile) { - if(qmFile == DEFAULT_LANG_CODE) + if (qmFile == DEFAULT_LANG_CODE) + { return DEFAULT_LANG_NAME; + } QTranslator translator; translator.load(translationPrefix + "_" + qmFile + ".qm", translationPath); @@ -173,8 +193,7 @@ void IntroPage::retranslateUi() versionLabel->setText(tr("Version:") + QString(" %1").arg(VERSION_STRING)); } -LoadSetsPage::LoadSetsPage(QWidget *parent) - : OracleWizardPage(parent), nam(0) +LoadSetsPage::LoadSetsPage(QWidget *parent) : OracleWizardPage(parent), nam(nullptr) { urlRadioButton = new QRadioButton(this); fileRadioButton = new QRadioButton(this); @@ -193,7 +212,7 @@ LoadSetsPage::LoadSetsPage(QWidget *parent) fileButton = new QPushButton(this); connect(fileButton, SIGNAL(clicked()), this, SLOT(actLoadSetsFile())); - QGridLayout *layout = new QGridLayout(this); + auto *layout = new QGridLayout(this); layout->addWidget(urlRadioButton, 0, 0); layout->addWidget(urlLineEdit, 0, 1); layout->addWidget(urlButton, 1, 1, Qt::AlignRight); @@ -245,11 +264,15 @@ void LoadSetsPage::actLoadSetsFile() dialog.setNameFilter(tr("Sets JSON file (*.json)")); #endif - if(!fileLineEdit->text().isEmpty() && QFile::exists(fileLineEdit->text())) + if (!fileLineEdit->text().isEmpty() && QFile::exists(fileLineEdit->text())) + { dialog.selectFile(fileLineEdit->text()); + } if (!dialog.exec()) + { return; + } fileLineEdit->setText(dialog.selectedFiles().at(0)); } @@ -257,14 +280,16 @@ void LoadSetsPage::actLoadSetsFile() bool LoadSetsPage::validatePage() { // once the import is finished, we call next(); skip validation - if(wizard()->importer->getSets().count() > 0) + if (wizard()->importer->getSets().count() > 0) + { return true; + } // else, try to import sets - if(urlRadioButton->isChecked()) + if (urlRadioButton->isChecked()) { QUrl url = QUrl::fromUserInput(urlLineEdit->text()); - if(!url.isValid()) + if (!url.isValid()) { QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid.")); return false; @@ -282,16 +307,19 @@ bool LoadSetsPage::validatePage() setEnabled(false); downloadSetsFile(url); - } else if(fileRadioButton->isChecked()) { + } + else if (fileRadioButton->isChecked()) + { QFile setsFile(fileLineEdit->text()); - if(!setsFile.exists()) + if (!setsFile.exists()) { QMessageBox::critical(this, tr("Error"), tr("Please choose a file.")); return false; } - if (!setsFile.open(QIODevice::ReadOnly)) { - QMessageBox::critical(0, tr("Error"), tr("Cannot open file '%1'.").arg(fileLineEdit->text())); + if (!setsFile.open(QIODevice::ReadOnly)) + { + QMessageBox::critical(nullptr, tr("Error"), tr("Cannot open file '%1'.").arg(fileLineEdit->text())); return false; } @@ -301,13 +329,16 @@ bool LoadSetsPage::validatePage() readSetsFromByteArray(setsFile.readAll()); } + return false; } void LoadSetsPage::downloadSetsFile(QUrl url) { - if(!nam) + if (!nam) + { nam = new QNetworkAccessManager(this); + } QNetworkReply *reply = nam->get(QNetworkRequest(url)); connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSetsFile())); @@ -316,10 +347,10 @@ void LoadSetsPage::downloadSetsFile(QUrl url) void LoadSetsPage::actDownloadProgressSetsFile(qint64 received, qint64 total) { - if(total > 0) + if (total > 0) { - progressBar->setMaximum(total); - progressBar->setValue(received); + progressBar->setMaximum(static_cast(total)); + progressBar->setValue(static_cast(received)); } progressLabel->setText(tr("Downloading (%1MB)").arg((int) received / (1024 * 1024))); } @@ -327,9 +358,10 @@ void LoadSetsPage::actDownloadProgressSetsFile(qint64 received, qint64 total) void LoadSetsPage::actDownloadFinishedSetsFile() { // check for a reply - QNetworkReply *reply = static_cast(sender()); + auto *reply = dynamic_cast(sender()); QNetworkReply::NetworkError errorCode = reply->error(); - if (errorCode != QNetworkReply::NoError) { + if (errorCode != QNetworkReply::NoError) + { QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString())); wizard()->enableButtons(); @@ -340,7 +372,8 @@ void LoadSetsPage::actDownloadFinishedSetsFile() } int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - if (statusCode == 301 || statusCode == 302) { + if (statusCode == 301 || statusCode == 302) + { QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); qDebug() << "following redirect url:" << redirectUrl.toString(); downloadSetsFile(redirectUrl); @@ -352,10 +385,14 @@ void LoadSetsPage::actDownloadFinishedSetsFile() progressBar->hide(); // save allsets.json url, but only if the user customized it and download was successfull - if(urlLineEdit->text() != QString(ALLSETS_URL)) + if (urlLineEdit->text() != QString(ALLSETS_URL)) + { wizard()->settings->setValue("allsetsurl", urlLineEdit->text()); + } else + { wizard()->settings->remove("allsetsurl"); + } readSetsFromByteArray(reply->readAll()); reply->deleteLater(); @@ -376,19 +413,20 @@ void LoadSetsPage::readSetsFromByteArray(QByteArray data) { #ifdef HAS_ZLIB // zipped file - QBuffer *inBuffer = new QBuffer(&data); - QBuffer *outBuffer = new QBuffer(this); + auto *inBuffer = new QBuffer(&data); + auto *outBuffer = new QBuffer(this); QString fileName; UnZip::ErrorCode ec; UnZip uz; ec = uz.openArchive(inBuffer); - if (ec != UnZip::Ok) { + if (ec != UnZip::Ok) + { zipDownloadFailed(tr("Failed to open Zip archive: %1.").arg(uz.formatError(ec))); return; } - if(uz.fileList().size() != 1) + if (uz.fileList().size() != 1) { zipDownloadFailed(tr("Zip extraction failed: the Zip archive doesn't contain exactly one file.")); return; @@ -397,7 +435,8 @@ void LoadSetsPage::readSetsFromByteArray(QByteArray data) outBuffer->open(QBuffer::ReadWrite); ec = uz.extractFile(fileName, outBuffer); - if (ec != UnZip::Ok) { + if (ec != UnZip::Ok) + { zipDownloadFailed(tr("Zip extraction failed: %1.").arg(uz.formatError(ec))); uz.closeArchive(); return; @@ -429,7 +468,8 @@ void LoadSetsPage::zipDownloadFailed(const QString &message) progressBar->hide(); QMessageBox::StandardButton reply; - reply = QMessageBox::question(this, tr("Error"), message + "
" + tr("Do you want to try to download a fresh copy of the uncompressed file instead?"), QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes); + reply = static_cast(QMessageBox::question(this, tr("Error"), message + "
" + tr("Do you want to try to download a fresh copy of the uncompressed file instead?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)); + if (reply == QMessageBox::Yes) { urlRadioButton->setChecked(true); @@ -446,16 +486,17 @@ void LoadSetsPage::importFinished() progressLabel->hide(); progressBar->hide(); - if(watcher.future().result()) + if (watcher.future().result()) { wizard()->next(); - } else { + } + else + { QMessageBox::critical(this, tr("Error"), tr("The file was retrieved successfully, but it does not contain any sets data.")); } } -SaveSetsPage::SaveSetsPage(QWidget *parent) - : OracleWizardPage(parent) +SaveSetsPage::SaveSetsPage(QWidget *parent) : OracleWizardPage(parent) { defaultPathCheckBox = new QCheckBox(this); defaultPathCheckBox->setChecked(true); @@ -463,7 +504,7 @@ SaveSetsPage::SaveSetsPage(QWidget *parent) messageLog = new QTextEdit(this); messageLog->setReadOnly(true); - QGridLayout *layout = new QGridLayout(this); + auto *layout = new QGridLayout(this); layout->addWidget(defaultPathCheckBox, 0, 0); layout->addWidget(messageLog, 1, 0); @@ -472,7 +513,7 @@ SaveSetsPage::SaveSetsPage(QWidget *parent) void SaveSetsPage::cleanupPage() { - disconnect(wizard()->importer, SIGNAL(setIndexChanged(int, int, const QString &)), 0, 0); + disconnect(wizard()->importer, SIGNAL(setIndexChanged(int, int, const QString &)), nullptr, nullptr); } void SaveSetsPage::initializePage() @@ -482,7 +523,9 @@ void SaveSetsPage::initializePage() connect(wizard()->importer, SIGNAL(setIndexChanged(int, int, const QString &)), this, SLOT(updateTotalProgress(int, int, const QString &))); if (!wizard()->importer->startImport()) + { QMessageBox::critical(this, tr("Error"), tr("No set has been imported.")); + } } void SaveSetsPage::retranslateUi() @@ -496,11 +539,15 @@ void SaveSetsPage::retranslateUi() void SaveSetsPage::updateTotalProgress(int cardsImported, int /* setIndex */, const QString &setName) { - if (setName.isEmpty()) { + if (setName.isEmpty()) + { messageLog->append("" + tr("Import finished: %1 cards.").arg(wizard()->importer->getCardList().size()) + ""); - } else { + } + else + { messageLog->append(tr("%1: %2 cards imported").arg(setName).arg(cardsImported)); } + messageLog->verticalScrollBar()->setValue(messageLog->verticalScrollBar()->maximum()); } @@ -511,39 +558,51 @@ bool SaveSetsPage::validatePage() QString windowName = tr("Save card database"); QString fileType = tr("XML; card database (*.xml)"); - do { + do + { QString fileName; if (defaultPathCheckBox->isChecked()) + { fileName = defaultPath; + } else + { fileName = QFileDialog::getSaveFileName(this, windowName, defaultPath, fileType); + } if (fileName.isEmpty()) + { return false; + } QFileInfo fi(fileName); QDir fileDir(fi.path()); - if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) { + if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) + { return false; } + if (wizard()->importer->saveToFile(fileName)) { ok = true; QMessageBox::information(this, tr("Success"), tr("The card database has been saved successfully to\n%1").arg(fileName)); - } else { + } + else + { QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName));; if (defaultPathCheckBox->isChecked()) + { defaultPathCheckBox->setChecked(false); + } } } while (!ok); return true; } -LoadTokensPage::LoadTokensPage(QWidget *parent) - : OracleWizardPage(parent), nam(0) +LoadSpoilersPage::LoadSpoilersPage(QWidget *parent) : OracleWizardPage(parent), nam(nullptr) { urlLabel = new QLabel(this); urlLineEdit = new QLineEdit(this); @@ -554,7 +613,152 @@ LoadTokensPage::LoadTokensPage(QWidget *parent) urlButton = new QPushButton(this); connect(urlButton, SIGNAL(clicked()), this, SLOT(actRestoreDefaultUrl())); - QGridLayout *layout = new QGridLayout(this); + auto *layout = new QGridLayout(this); + layout->addWidget(urlLabel, 0, 0); + layout->addWidget(urlLineEdit, 0, 1); + layout->addWidget(urlButton, 1, 1, Qt::AlignRight); + layout->addWidget(progressLabel, 2, 0); + layout->addWidget(progressBar, 2, 1); +} + +void LoadSpoilersPage::actRestoreDefaultUrl() +{ + urlLineEdit->setText(SPOILERS_URL); +} + +void LoadSpoilersPage::initializePage() +{ + urlLineEdit->setText(wizard()->settings->value("spoilersurl", SPOILERS_URL).toString()); + + progressLabel->hide(); + progressBar->hide(); +} + +void LoadSpoilersPage::actDownloadProgressSpoilersFile(qint64 received, qint64 total) +{ + if (total > 0) + { + progressBar->setMaximum(static_cast(total)); + progressBar->setValue(static_cast(received)); + } + + progressLabel->setText(tr("Downloading (%1MB)").arg((int) received / (1024 * 1024))); +} + +void LoadSpoilersPage::actDownloadFinishedSpoilersFile() +{ + // Check for server reply + auto *reply = dynamic_cast(sender()); + QNetworkReply::NetworkError errorCode = reply->error(); + + if (errorCode != QNetworkReply::NoError) + { + QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString())); + + wizard()->enableButtons(); + setEnabled(true); + + reply->deleteLater(); + return; + } + + int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (statusCode == 301 || statusCode == 302) + { + QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + qDebug() << "following redirect url:" << redirectUrl.toString(); + downloadSpoilersFile(redirectUrl); + reply->deleteLater(); + return; + } + + progressLabel->hide(); + progressBar->hide(); + + // save spoiler.xml url, but only if the user customized it and download was successful + if (urlLineEdit->text() != QString(SPOILERS_URL)) + { + wizard()->settings->setValue("spoilersurl", urlLineEdit->text()); + } + else + { + wizard()->settings->remove("spoilersurl"); + } + + wizard()->setTokensData(reply->readAll()); + reply->deleteLater(); + + wizard()->enableButtons(); + setEnabled(true); + progressLabel->hide(); + progressBar->hide(); + + wizard()->next(); +} + +void LoadSpoilersPage::downloadSpoilersFile(QUrl url) +{ + if (!nam) + { + nam = new QNetworkAccessManager(this); + } + QNetworkReply *reply = nam->get(QNetworkRequest(url)); + + connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSpoilersFile())); + connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(actDownloadProgressSpoilersFile(qint64, qint64))); +} + +bool LoadSpoilersPage::validatePage() +{ + // once the import is finished, we call next(); skip validation + if (wizard()->hasTokensData()) + { + return true; + } + + QUrl url = QUrl::fromUserInput(urlLineEdit->text()); + if (!url.isValid()) + { + QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid.")); + return false; + } + + progressLabel->setText(tr("Downloading (0MB)")); + // show an infinite progressbar + progressBar->setMaximum(0); + progressBar->setMinimum(0); + progressBar->setValue(0); + progressLabel->show(); + progressBar->show(); + + wizard()->disableButtons(); + setEnabled(false); + + downloadSpoilersFile(url); + return false; +} + +void LoadSpoilersPage::retranslateUi() +{ + setTitle(tr("Spoilers source selection")); + setSubTitle(tr("Please specify a spoiler source.")); + + urlLabel->setText(tr("Download URL:")); + urlButton->setText(tr("Restore default URL")); +} + +LoadTokensPage::LoadTokensPage(QWidget *parent) : OracleWizardPage(parent), nam(nullptr) +{ + urlLabel = new QLabel(this); + urlLineEdit = new QLineEdit(this); + + progressLabel = new QLabel(this); + progressBar = new QProgressBar(this); + + urlButton = new QPushButton(this); + connect(urlButton, SIGNAL(clicked()), this, SLOT(actRestoreDefaultUrl())); + + auto *layout = new QGridLayout(this); layout->addWidget(urlLabel, 0, 0); layout->addWidget(urlLineEdit, 0, 1); layout->addWidget(urlButton, 1, 1, Qt::AlignRight); @@ -589,11 +793,13 @@ void LoadTokensPage::actRestoreDefaultUrl() bool LoadTokensPage::validatePage() { // once the import is finished, we call next(); skip validation - if(wizard()->hasTokensData()) + if (wizard()->hasTokensData()) + { return true; + } QUrl url = QUrl::fromUserInput(urlLineEdit->text()); - if(!url.isValid()) + if (!url.isValid()) { QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid.")); return false; @@ -616,8 +822,10 @@ bool LoadTokensPage::validatePage() void LoadTokensPage::downloadTokensFile(QUrl url) { - if(!nam) + if (!nam) + { nam = new QNetworkAccessManager(this); + } QNetworkReply *reply = nam->get(QNetworkRequest(url)); connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedTokensFile())); @@ -626,10 +834,10 @@ void LoadTokensPage::downloadTokensFile(QUrl url) void LoadTokensPage::actDownloadProgressTokensFile(qint64 received, qint64 total) { - if(total > 0) + if (total > 0) { - progressBar->setMaximum(total); - progressBar->setValue(received); + progressBar->setMaximum(static_cast(total)); + progressBar->setValue(static_cast(received)); } progressLabel->setText(tr("Downloading (%1MB)").arg((int) received / (1024 * 1024))); } @@ -637,9 +845,10 @@ void LoadTokensPage::actDownloadProgressTokensFile(qint64 received, qint64 total void LoadTokensPage::actDownloadFinishedTokensFile() { // check for a reply - QNetworkReply *reply = static_cast(sender()); + auto *reply = dynamic_cast(sender()); QNetworkReply::NetworkError errorCode = reply->error(); - if (errorCode != QNetworkReply::NoError) { + if (errorCode != QNetworkReply::NoError) + { QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString())); wizard()->enableButtons(); @@ -650,7 +859,8 @@ void LoadTokensPage::actDownloadFinishedTokensFile() } int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - if (statusCode == 301 || statusCode == 302) { + if (statusCode == 301 || statusCode == 302) + { QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); qDebug() << "following redirect url:" << redirectUrl.toString(); downloadTokensFile(redirectUrl); @@ -662,10 +872,14 @@ void LoadTokensPage::actDownloadFinishedTokensFile() progressBar->hide(); // save tokens.xml url, but only if the user customized it and download was successfull - if(urlLineEdit->text() != QString(TOKENS_URL)) + if (urlLineEdit->text() != QString(TOKENS_URL)) + { wizard()->settings->setValue("tokensurl", urlLineEdit->text()); + } else + { wizard()->settings->remove("tokensurl"); + } wizard()->setTokensData(reply->readAll()); reply->deleteLater(); @@ -678,13 +892,81 @@ void LoadTokensPage::actDownloadFinishedTokensFile() wizard()->next(); } -SaveTokensPage::SaveTokensPage(QWidget *parent) - : OracleWizardPage(parent) +SaveSpoilersPage::SaveSpoilersPage(QWidget *parent) : OracleWizardPage(parent) { defaultPathCheckBox = new QCheckBox(this); defaultPathCheckBox->setChecked(true); - QGridLayout *layout = new QGridLayout(this); + auto *layout = new QGridLayout(this); + layout->addWidget(defaultPathCheckBox, 0, 0); + + setLayout(layout); + +} + +void SaveSpoilersPage::retranslateUi() +{ + setTitle(tr("Spoilers imported")); + setSubTitle(tr("The spoilers file has been imported. " + "Press \"Save\" to save the imported spoilers to the Cockatrice card database.")); + + defaultPathCheckBox->setText(tr("Save to the default path (recommended)")); +} + +bool SaveSpoilersPage::validatePage() +{ + bool ok = false; + QString defaultPath = settingsCache->getSpoilerCardDatabasePath(); + QString windowName = tr("Save spoiler database"); + QString fileType = tr("XML; card database (*.xml)"); + + do + { + QString fileName; + if (defaultPathCheckBox->isChecked()) + { + fileName = defaultPath; + } + else + { + fileName = QFileDialog::getSaveFileName(this, windowName, defaultPath, fileType); + } + + if (fileName.isEmpty()) + { + return false; + } + + QFileInfo fi(fileName); + QDir fileDir(fi.path()); + if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) + { + return false; + } + + if (wizard()->saveTokensToFile(fileName)) + { + ok = true; + } + else + { + QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName));; + if (defaultPathCheckBox->isChecked()) + { + defaultPathCheckBox->setChecked(false); + } + } + } while (!ok); + + return true; +} + +SaveTokensPage::SaveTokensPage(QWidget *parent) : OracleWizardPage(parent) +{ + defaultPathCheckBox = new QCheckBox(this); + defaultPathCheckBox->setChecked(true); + + auto *layout = new QGridLayout(this); layout->addWidget(defaultPathCheckBox, 0, 0); setLayout(layout); @@ -706,33 +988,47 @@ bool SaveTokensPage::validatePage() QString windowName = tr("Save token database"); QString fileType = tr("XML; token database (*.xml)"); - do { + do + { QString fileName; if (defaultPathCheckBox->isChecked()) + { fileName = defaultPath; + } else + { fileName = QFileDialog::getSaveFileName(this, windowName, defaultPath, fileType); + } if (fileName.isEmpty()) + { return false; + } + QFileInfo fi(fileName); QDir fileDir(fi.path()); - if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) { + if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) + { return false; } + if (wizard()->saveTokensToFile(fileName)) { ok = true; QMessageBox::information(this, tr("Success"), tr("The token database has been saved successfully to\n%1").arg(fileName)); - } else { + } + else + { QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName));; if (defaultPathCheckBox->isChecked()) + { defaultPathCheckBox->setChecked(false); + } } } while (!ok); return true; -} +} \ No newline at end of file diff --git a/oracle/src/oraclewizard.h b/oracle/src/oraclewizard.h index 13399b98..d7e812f6 100644 --- a/oracle/src/oraclewizard.h +++ b/oracle/src/oraclewizard.h @@ -4,6 +4,7 @@ #include #include #include +#include class QCheckBox; class QGroupBox; @@ -20,139 +21,195 @@ class QSettings; class OracleWizard : public QWizard { - Q_OBJECT -public: - OracleWizard(QWidget *parent = 0); - void accept(); - void enableButtons(); - void disableButtons(); - void retranslateUi(); - void setTokensData(QByteArray _tokensData) { tokensData = _tokensData; } - bool hasTokensData() { return !tokensData.isEmpty(); } - bool saveTokensToFile(const QString & fileName); -public: - OracleImporter *importer; - QSettings * settings; -private slots: - void updateLanguage(); -private: - QStringList findQmFiles(); - QString languageName(const QString &qmFile); - QByteArray tokensData; -protected: - void changeEvent(QEvent *event); -}; + Q_OBJECT + public: + explicit OracleWizard(QWidget *parent = nullptr); + void accept() override; + void enableButtons(); + void disableButtons(); + void retranslateUi(); + void setTokensData(QByteArray _tokensData) { tokensData = std::move(_tokensData); } + bool hasTokensData() { return !tokensData.isEmpty(); } + bool saveTokensToFile(const QString & fileName); + public: + OracleImporter *importer; + QSettings *settings; + + private slots: + void updateLanguage(); + + private: + QByteArray tokensData; + + protected: + void changeEvent(QEvent *event) override; +}; class OracleWizardPage : public QWizardPage { - Q_OBJECT -public: - OracleWizardPage(QWidget *parent = 0): QWizardPage(parent) {}; - virtual void retranslateUi() = 0; -protected: - inline OracleWizard *wizard() { return (OracleWizard*) QWizardPage::wizard(); }; + Q_OBJECT + public: + explicit OracleWizardPage(QWidget *parent = nullptr): QWizardPage(parent) {}; + virtual void retranslateUi() = 0; + + protected: + inline OracleWizard *wizard() { return (OracleWizard*) QWizardPage::wizard(); }; }; class IntroPage : public OracleWizardPage { - Q_OBJECT -public: - IntroPage(QWidget *parent = 0); - void retranslateUi(); -private: - QStringList findQmFiles(); - QString languageName(const QString &qmFile); -private: - QLabel *label, *languageLabel, *versionLabel; - QComboBox *languageBox; -private slots: - void languageBoxChanged(int index); + Q_OBJECT + public: + explicit IntroPage(QWidget *parent = nullptr); + void retranslateUi() override; + + private: + QStringList findQmFiles(); + QString languageName(const QString &qmFile); + + private: + QLabel *label, *languageLabel, *versionLabel; + QComboBox *languageBox; + + private slots: + void languageBoxChanged(int index); }; class LoadSetsPage : public OracleWizardPage { Q_OBJECT -public: - LoadSetsPage(QWidget *parent = 0); - void retranslateUi(); -protected: - void initializePage(); - bool validatePage(); - void readSetsFromByteArray(QByteArray data); - void downloadSetsFile(QUrl url); -private: - QRadioButton *urlRadioButton; - QRadioButton *fileRadioButton; - QLineEdit *urlLineEdit; - QLineEdit *fileLineEdit; - QPushButton *urlButton; - QPushButton *fileButton; - QLabel *progressLabel; - QProgressBar * progressBar; + public: + explicit LoadSetsPage(QWidget *parent = nullptr); + void retranslateUi() override; - QNetworkAccessManager *nam; - QFutureWatcher watcher; - QFuture future; -private slots: - void actLoadSetsFile(); - void actRestoreDefaultUrl(); - void actDownloadProgressSetsFile(qint64 received, qint64 total); - void actDownloadFinishedSetsFile(); - void importFinished(); - void zipDownloadFailed(const QString &message); + protected: + void initializePage() override; + bool validatePage() override; + void readSetsFromByteArray(QByteArray data); + void downloadSetsFile(QUrl url); + + private: + QRadioButton *urlRadioButton; + QRadioButton *fileRadioButton; + QLineEdit *urlLineEdit; + QLineEdit *fileLineEdit; + QPushButton *urlButton; + QPushButton *fileButton; + QLabel *progressLabel; + QProgressBar *progressBar; + + QNetworkAccessManager *nam; + QFutureWatcher watcher; + QFuture future; + + private slots: + void actLoadSetsFile(); + void actRestoreDefaultUrl(); + void actDownloadProgressSetsFile(qint64 received, qint64 total); + void actDownloadFinishedSetsFile(); + void importFinished(); + void zipDownloadFailed(const QString &message); }; class SaveSetsPage : public OracleWizardPage { - Q_OBJECT -public: - SaveSetsPage(QWidget *parent = 0); - void retranslateUi(); -private: - QTextEdit *messageLog; - QCheckBox * defaultPathCheckBox; -protected: - void initializePage(); - void cleanupPage(); - bool validatePage(); -private slots: - void updateTotalProgress(int cardsImported, int setIndex, const QString &setName); + Q_OBJECT + public: + explicit SaveSetsPage(QWidget *parent = nullptr); + void retranslateUi() override; + + private: + QTextEdit *messageLog; + QCheckBox *defaultPathCheckBox; + + protected: + void initializePage() override; + void cleanupPage() override; + bool validatePage() override; + + private slots: + void updateTotalProgress(int cardsImported, int setIndex, const QString &setName); +}; + +class LoadSpoilersPage : public OracleWizardPage +{ + Q_OBJECT + public: + explicit LoadSpoilersPage(QWidget *parent = nullptr); + void retranslateUi() override; + + private: + QLabel *urlLabel; + QLineEdit *urlLineEdit; + QPushButton *urlButton; + QLabel *progressLabel; + QProgressBar *progressBar; + QNetworkAccessManager *nam; + + private slots: + void actRestoreDefaultUrl(); + void actDownloadProgressSpoilersFile(qint64 received, qint64 total); + void actDownloadFinishedSpoilersFile(); + + protected: + void initializePage() override; + bool validatePage() override; + void downloadSpoilersFile(QUrl url); +}; + +class SaveSpoilersPage : public OracleWizardPage +{ + Q_OBJECT + public: + explicit SaveSpoilersPage(QWidget *parent = nullptr); + void retranslateUi() override; + + private: + QCheckBox *defaultPathCheckBox; + + protected: + bool validatePage() override; }; class LoadTokensPage : public OracleWizardPage { - Q_OBJECT -public: - LoadTokensPage(QWidget *parent = 0); - void retranslateUi(); -protected: - void initializePage(); - bool validatePage(); - void downloadTokensFile(QUrl url); -private: - QLabel *urlLabel; - QLineEdit *urlLineEdit; - QPushButton *urlButton; - QLabel *progressLabel; - QProgressBar * progressBar; + Q_OBJECT + public: + explicit LoadTokensPage(QWidget *parent = nullptr); + void retranslateUi() override; - QNetworkAccessManager *nam; -private slots: - void actRestoreDefaultUrl(); - void actDownloadProgressTokensFile(qint64 received, qint64 total); - void actDownloadFinishedTokensFile(); + protected: + void initializePage() override; + bool validatePage() override; + void downloadTokensFile(QUrl url); + + private: + QLabel *urlLabel; + QLineEdit *urlLineEdit; + QPushButton *urlButton; + QLabel *progressLabel; + QProgressBar *progressBar; + QNetworkAccessManager *nam; + + private slots: + void actRestoreDefaultUrl(); + void actDownloadProgressTokensFile(qint64 received, qint64 total); + void actDownloadFinishedTokensFile(); }; class SaveTokensPage : public OracleWizardPage { - Q_OBJECT -public: - SaveTokensPage(QWidget *parent = 0); - void retranslateUi(); -private: - QCheckBox * defaultPathCheckBox; -protected: - bool validatePage(); + Q_OBJECT + public: + explicit SaveTokensPage(QWidget *parent = nullptr); + void retranslateUi() override; + + private: + QCheckBox *defaultPathCheckBox; + + protected: + bool validatePage() override; }; + #endif \ No newline at end of file diff --git a/tests/carddatabase/carddatabase_test.cpp b/tests/carddatabase/carddatabase_test.cpp index 66fcad32..20772ba1 100644 --- a/tests/carddatabase/carddatabase_test.cpp +++ b/tests/carddatabase/carddatabase_test.cpp @@ -14,6 +14,7 @@ SettingsCache::~SettingsCache() { delete cardDatabaseSettings; }; QString SettingsCache::getCustomCardDatabasePath() const { return QString("%1/customsets/").arg(CARDDB_DATADIR); } QString SettingsCache::getCardDatabasePath() const { return QString("%1/cards.xml").arg(CARDDB_DATADIR); } QString SettingsCache::getTokenDatabasePath() const { return QString("%1/tokens.xml").arg(CARDDB_DATADIR); } +QString SettingsCache::getSpoilerCardDatabasePath() const { return QString("%1/spoiler.xml").arg(CARDDB_DATADIR); } CardDatabaseSettings& SettingsCache::cardDatabase() const { return *cardDatabaseSettings; } SettingsCache *settingsCache; diff --git a/tests/carddatabase/carddatabase_test.h b/tests/carddatabase/carddatabase_test.h index a29b2857..28086e4d 100644 --- a/tests/carddatabase/carddatabase_test.h +++ b/tests/carddatabase/carddatabase_test.h @@ -30,6 +30,7 @@ public: QString getCustomCardDatabasePath() const; QString getCardDatabasePath() const; QString getTokenDatabasePath() const; + QString getSpoilerCardDatabasePath() const; CardDatabaseSettings& cardDatabase() const; signals: void cardDatabasePathChanged(); diff --git a/tests/carddatabase/data/spoilers.xml b/tests/carddatabase/data/spoilers.xml new file mode 100644 index 00000000..1777a803 --- /dev/null +++ b/tests/carddatabase/data/spoilers.xml @@ -0,0 +1,17 @@ + + + + + Fluffy + CAT + G + + + Token + 0/1 + 0 + + 1 + + +