Merge branch 'master' into valgrind

This commit is contained in:
Fabio Bas 2014-08-09 11:38:53 +02:00
commit 954489e6c0
105 changed files with 5903 additions and 4729 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@ tags
build* build*
*.qm *.qm
.directory .directory
mysql.cnf

View file

@ -76,8 +76,19 @@ IF(MSVC)
#set(CMAKE_CXX_FLAGS_DEBUG "/Zi") #set(CMAKE_CXX_FLAGS_DEBUG "/Zi")
ELSEIF (CMAKE_COMPILER_IS_GNUCXX) ELSEIF (CMAKE_COMPILER_IS_GNUCXX)
# linux/gcc, bsd/gcc, windows/mingw # linux/gcc, bsd/gcc, windows/mingw
include(CheckCXXCompilerFlag)
set(CMAKE_CXX_FLAGS_RELEASE "-s -O2") set(CMAKE_CXX_FLAGS_RELEASE "-s -O2")
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -Wall -Wextra -pedantic -Werror -Wcast-align -Wmissing-declarations -Winline -Wno-long-long -Wno-error=extra -Wno-error=unused-parameter -Wno-inline -Wno-error=delete-non-virtual-dtor -Wno-error=sign-compare -Wno-error=reorder -Wno-error=missing-declarations") set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -Wall -Wextra -pedantic -Werror")
set(ADDITIONAL_DEBUG_FLAGS -Wcast-align -Wmissing-declarations -Winline -Wno-long-long -Wno-error=extra -Wno-error=unused-parameter -Wno-inline -Wno-error=delete-non-virtual-dtor -Wno-error=sign-compare -Wno-error=reorder -Wno-error=missing-declarations)
FOREACH(FLAG ${ADDITIONAL_DEBUG_FLAGS})
CHECK_CXX_COMPILER_FLAG("${FLAG}" CXX_HAS_WARNING_${FLAG})
IF(CXX_HAS_WARNING_${FLAG})
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAG}")
ENDIF()
ENDFOREACH()
ELSE() ELSE()
# other: osx/llvm, bsd/llvm # other: osx/llvm, bsd/llvm
set(CMAKE_CXX_FLAGS_RELEASE "-O2") set(CMAKE_CXX_FLAGS_RELEASE "-O2")
@ -99,6 +110,11 @@ ENDIF()
IF(Qt5Widgets_FOUND) IF(Qt5Widgets_FOUND)
MESSAGE(STATUS "Found Qt ${Qt5Widgets_VERSION_STRING}") MESSAGE(STATUS "Found Qt ${Qt5Widgets_VERSION_STRING}")
if(UNIX AND NOT APPLE AND "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64")
# FIX: Qt was built with -reduce-relocations
add_definitions(-fPIC)
endif()
ELSE() ELSE()
FIND_PACKAGE(Qt4 4.8.0 REQUIRED) FIND_PACKAGE(Qt4 4.8.0 REQUIRED)
IF(QT4_FOUND) IF(QT4_FOUND)

134
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,134 @@
# Style Guide #
### Compatibility ###
Cockatrice is written in C++-03, so do not use C++11 constructs such as `auto`.
Cockatrice support both Qt 4 and Qt 5, so make sure that your code compiles
with both. If there have been breaking changes between Qt 4 and 5, use a
statement such as
#if QT_VERSION >= 0x500000
doSomethingWithQt5();
#else
doSomethingWithQt4();
#endif
For consistency, use Qt data structures where possible, such as `QString` over
`std::string` or `QList` over `std::vector`.
### Header files ###
Use header files with the extension `.h` and source files with the extension
`.cpp`.
Use header guards in the form of `FILE_NAME_H`.
Simple functions, such as getters, may be written inline in the header file,
but other functions should be written in the source file.
Keep library includes and project includes grouped together. So this is okay:
// Good:
#include <QList>
#include <QString>
#include "card.h"
#include "deck.h"
// Good:
#include "card.h"
#include "deck.h"
#include <QList>
#include <QString>
// Bad:
#include <QList>
#include "card.h"
#include <QString>
#include "deck.h"
### Naming ###
Use `UpperCamelCase` for classes, structs, enums, etc. and `lowerCamelCase` for
function and variable names.
Member variables aren't decorated in any way. Don't prefix or suffix with
underscores, etc.
For arguments to constructors which have the same names as member variables,
prefix those arguments with underscores:
MyClass::MyClass(int _myData)
: myData(_myData)
{}
Pointers and references should be denoted with the `*` or `&` going with the
variable name:
// Good:
Foo *foo1 = new Foo;
Foo &foo2 = *foo1;
// Bad:
Bar* bar1 = new Bar;
Bar& bar2 = *bar1;
Use `0` instead of `NULL` (or `nullptr`) for null pointers.
### Braces ###
Use K&R-style braces. Braces for function implementations go on their own
lines, but they go on the same line everywhere else:
int main()
{
if (someCondition) {
doSomething();
} else {
while (someOtherCondition) {
doSomethingElse();
}
}
}
Braces can be omitted for single-statement if's and the like, as long as it is
still legible.
### Tabs ###
Use only spaces. Four spaces per tab.
### Lines ###
Do not have trailing whitespace in your lines.
Lines should be 80 characters or less, as a soft limit.
### Memory Management ###
New code should be written using references over pointers and stack allocation
over heap allocation wherever possible.
// Good: uses stack allocation and references
void showCard(const Card &card);
int main()
{
Card card;
showCard(card);
}
// Bad: relies on manual memory management and doesn't give us much
// null-safety.
void showCard(const Card *card);
int main()
{
Card *card = new Card;
showCard(card);
delete card;
}
(Remember to pass by `const` reference wherever possible, to avoid accidentally
mutating objects.)
When pointers can't be avoided, try to use a smart pointer of some sort, such
as `QScopedPointer`, or, less preferably, `QSharedPointer`.

View file

@ -33,8 +33,11 @@ To compile:
The following flags can be passed to `cmake`: The following flags can be passed to `cmake`:
- `-DWITH_SERVER=1` build the server - `-DWITH_SERVER=1` Build the server
- `-DWITHOUT_CLIENT=1` do not build the client - `-DWITH_CLIENT=0` Do not build the client
- `-DWITH_ORACLE=0` Do not build Oracle
- `-DWITH_QT4=1` Force compilation to use Qt4 instead of Qt5.
- `-DCMAKE_BUILD_TYPE=Debug` Compile in debug mode. Enables extra logging output, debug symbols, and much more verbose compiler warnings.
# Running # Running

View file

@ -1,3 +1 @@
!define NSIS_PROJECT_NAME "@PROJECT_NAME@"
!define NSIS_SOURCE_PATH "@PROJECT_SOURCE_DIR@" !define NSIS_SOURCE_PATH "@PROJECT_SOURCE_DIR@"
!define NSIS_BINARY_PATH "@PROJECT_BINARY_DIR@"

View file

@ -2,10 +2,11 @@
!include "MUI2.nsh" !include "MUI2.nsh"
!include "FileFunc.nsh" !include "FileFunc.nsh"
Name "${NSIS_PROJECT_NAME}" Name "@CPACK_PACKAGE_NAME@"
OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@"
SetCompressor /SOLID lzma SetCompressor /SOLID lzma
InstallDir "$PROGRAMFILES\Cockatrice" InstallDir "$PROGRAMFILES\Cockatrice"
!define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@"
!define MUI_ABORTWARNING !define MUI_ABORTWARNING
!define MUI_WELCOMEFINISHPAGE_BITMAP "${NSIS_SOURCE_PATH}\cmake\leftimage.bmp" !define MUI_WELCOMEFINISHPAGE_BITMAP "${NSIS_SOURCE_PATH}\cmake\leftimage.bmp"
@ -35,7 +36,8 @@ Section "Application" SecApplication
SetShellVarContext all SetShellVarContext all
SetOutPath "$INSTDIR" SetOutPath "$INSTDIR"
File /r "${NSIS_BINARY_PATH}\Release\*.*" @CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS@
@CPACK_NSIS_FULL_INSTALL@
WriteUninstaller "$INSTDIR\uninstall.exe" WriteUninstaller "$INSTDIR\uninstall.exe"
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2

View file

@ -2,6 +2,7 @@ find_package(Git)
if(GIT_FOUND) if(GIT_FOUND)
execute_process( execute_process(
COMMAND ${GIT_EXECUTABLE} describe --long --always COMMAND ${GIT_EXECUTABLE} describe --long --always
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE res_var RESULT_VARIABLE res_var
OUTPUT_VARIABLE GIT_COM_ID OUTPUT_VARIABLE GIT_COM_ID
) )

View file

@ -13,6 +13,7 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QDebug> #include <QDebug>
#include <QImageReader>
const int CardDatabase::versionNeeded = 3; const int CardDatabase::versionNeeded = 3;
@ -92,6 +93,14 @@ bool PictureToLoad::nextSet()
return true; return true;
} }
QString PictureToLoad::getSetName() const
{
if (setIndex < sortedSets.size())
return sortedSets[setIndex]->getCorrectedShortName();
else
return QString("");
}
PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent) PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent)
: QObject(parent), : QObject(parent),
_picsPath(__picsPath), picDownload(_picDownload), picDownloadHq(_picDownloadHq), _picsPath(__picsPath), picDownload(_picDownload), picDownloadHq(_picDownloadHq),
@ -124,14 +133,33 @@ void PictureLoader::processLoadQueue()
} }
PictureToLoad ptl = loadQueue.takeFirst(); PictureToLoad ptl = loadQueue.takeFirst();
mutex.unlock(); mutex.unlock();
QString correctedName = ptl.getCard()->getCorrectedName();
QString picsPath = _picsPath; //The list of paths to the folders in which to search for images
QString setName = ptl.getSetName(); QList<QString> picsPaths = QList<QString>() << _picsPath + "/CUSTOM/" + ptl.getCard()->getCorrectedName() + ".full";
QString setName=ptl.getSetName();
if(!setName.isEmpty())
{
picsPaths << _picsPath + "/" + setName + "/" + ptl.getCard()->getCorrectedName() + ".full"
<< _picsPath + "/downloadedPics/" + setName + "/" + ptl.getCard()->getCorrectedName() + ".full";
}
QImage image; QImage image;
if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg(setName).arg(correctedName))) QImageReader imgReader;
if (!image.load(QString("%1/%2/%3%4.full.jpg").arg(picsPath).arg(setName).arg(correctedName).arg(1))) imgReader.setDecideFormatFromContent(true);
if (!image.load(QString("%1/%2/%3/%4.full.jpg").arg(picsPath).arg("downloadedPics").arg(setName).arg(correctedName))) { bool found = false;
//Iterates through the list of paths, searching for images with the desired name with any QImageReader-supported extension
for (int i = 0; i < picsPaths.length() && !found; i ++) {
imgReader.setFileName(picsPaths.at(i));
if (imgReader.read(&image)) {
emit imageLoaded(ptl.getCard(), image);
found = true;
}
}
if (!found) {
if (picDownload) { if (picDownload) {
cardsToDownload.append(ptl); cardsToDownload.append(ptl);
if (!downloadRunning) if (!downloadRunning)
@ -142,23 +170,53 @@ void PictureLoader::processLoadQueue()
else else
emit imageLoaded(ptl.getCard(), QImage()); emit imageLoaded(ptl.getCard(), QImage());
} }
continue;
} }
emit imageLoaded(ptl.getCard(), image);
} }
} }
QString PictureLoader::getPicUrl(CardInfo *card) QString PictureLoader::getPicUrl(CardInfo *card)
{ {
if (!picDownload) return 0; if (!picDownload) return QString("");
QString picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl();
picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName()));
CardSet *set = card->getPreferredSet(); CardSet *set = card->getPreferredSet();
QString picUrl = QString("");
// if sets have been defined for the card, they can contain custom picUrls
if(set)
{
// first check if Hq is enabled and a custom Hq card url exists in cards.xml
if(picDownloadHq)
{
picUrl = card->getCustomPicURLHq(set->getShortName());
if (!picUrl.isEmpty())
return picUrl;
}
// then, test for a custom, non-Hq card url in cards.xml
picUrl = card->getCustomPicURL(set->getShortName());
if (!picUrl.isEmpty())
return picUrl;
}
// otherwise, fallback to the default url
picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl();
picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName()));
if (set) {
picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName())); picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName()));
picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName())); picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName()));
picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(card->getPreferredMuId()))); }
int muid = card->getPreferredMuId();
if (muid)
picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(muid)));
if (picUrl.contains("!name!") ||
picUrl.contains("!setcode!") ||
picUrl.contains("!setname!") ||
picUrl.contains("!cardid!")) {
qDebug() << "Insufficient card data to download" << card->getName() << "Url:" << picUrl;
return QString("");
}
return picUrl; return picUrl;
} }
@ -175,8 +233,13 @@ void PictureLoader::startNextPicDownload()
cardBeingDownloaded = cardsToDownload.takeFirst(); cardBeingDownloaded = cardsToDownload.takeFirst();
// TODO: Do something useful when picUrl is 0 or empty, etc
QString picUrl = getPicUrl(cardBeingDownloaded.getCard()); QString picUrl = getPicUrl(cardBeingDownloaded.getCard());
if (picUrl.isEmpty()) {
qDebug() << "No url for" << cardBeingDownloaded.getCard()->getName();
cardBeingDownloaded = 0;
downloadRunning = false;
return;
}
QUrl url(picUrl); QUrl url(picUrl);
@ -192,29 +255,35 @@ void PictureLoader::picDownloadFinished(QNetworkReply *reply)
qDebug() << "Download failed:" << reply->errorString(); qDebug() << "Download failed:" << reply->errorString();
} }
const QByteArray &picData = reply->readAll(); const QByteArray &picData = reply->peek(reply->size()); //peek is used to keep the data in the buffer for use by QImageReader
QImage testImage; QImage testImage;
if (testImage.loadFromData(picData)) {
if (!QDir(QString(picsPath + "/downloadedPics/")).exists()) { QImageReader imgReader;
QDir dir(picsPath); imgReader.setDecideFormatFromContent(true);
if (!dir.exists()) imgReader.setDevice(reply);
QString extension = "." + imgReader.format(); //the format is determined prior to reading the QImageReader data into a QImage object, as that wipes the QImageReader buffer
if (extension == ".jpeg")
extension = ".jpg";
if (imgReader.read(&testImage)) {
QString setName = cardBeingDownloaded.getSetName();
if(!setName.isEmpty())
{
if (!QDir().mkpath(picsPath + "/downloadedPics/" + setName)) {
qDebug() << picsPath + "/downloadedPics/" + setName + " could not be created.";
return; return;
dir.mkdir("downloadedPics");
}
if (!QDir(QString(picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName())).exists()) {
QDir dir(QString(picsPath + "/downloadedPics"));
dir.mkdir(cardBeingDownloaded.getSetName());
} }
QString suffix; QString suffix;
if (!cardBeingDownloaded.getStripped()) if (!cardBeingDownloaded.getStripped())
suffix = ".full"; suffix = ".full";
QFile newPic(picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName() + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + suffix + ".jpg"); QFile newPic(picsPath + "/downloadedPics/" + setName + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + suffix + extension);
if (!newPic.open(QIODevice::WriteOnly)) if (!newPic.open(QIODevice::WriteOnly))
return; return;
newPic.write(picData); newPic.write(picData);
newPic.close(); newPic.close();
}
emit imageLoaded(cardBeingDownloaded.getCard(), testImage); emit imageLoaded(cardBeingDownloaded.getCard(), testImage);
} else if (cardBeingDownloaded.getHq()) { } else if (cardBeingDownloaded.getHq()) {
@ -275,6 +344,8 @@ CardInfo::CardInfo(CardDatabase *_db,
bool _cipt, bool _cipt,
int _tableRow, int _tableRow,
const SetList &_sets, const SetList &_sets,
const QStringMap &_customPicURLs,
const QStringMap &_customPicURLsHq,
MuidMap _muIds) MuidMap _muIds)
: db(_db), : db(_db),
name(_name), name(_name),
@ -286,6 +357,8 @@ CardInfo::CardInfo(CardDatabase *_db,
text(_text), text(_text),
colors(_colors), colors(_colors),
loyalty(_loyalty), loyalty(_loyalty),
customPicURLs(_customPicURLs),
customPicURLsHq(_customPicURLsHq),
muIds(_muIds), muIds(_muIds),
cipt(_cipt), cipt(_cipt),
tableRow(_tableRow), tableRow(_tableRow),
@ -424,6 +497,8 @@ void CardInfo::updatePixmapCache()
CardSet* CardInfo::getPreferredSet() CardSet* CardInfo::getPreferredSet()
{ {
if(sets.isEmpty())
return 0;
SetList sortedSets = sets; SetList sortedSets = sets;
sortedSets.sortByKey(); sortedSets.sortByKey();
return sortedSets.first(); return sortedSets.first();
@ -431,7 +506,28 @@ CardSet* CardInfo::getPreferredSet()
int CardInfo::getPreferredMuId() int CardInfo::getPreferredMuId()
{ {
return muIds[getPreferredSet()->getShortName()]; CardSet *set = getPreferredSet();
return set ? muIds[set->getShortName()] : 0;
}
QString CardInfo::simplifyName(const QString &name) {
QString simpleName(name);
// So Aetherling would work, but not Ætherling since 'Æ' would get replaced
// with nothing.
simpleName.replace("æ", "ae");
simpleName.replace("Æ", "AE");
// Replace Jötun Grunt with Jotun Grunt.
simpleName = simpleName.normalized(QString::NormalizationForm_KD);
// Replace dashes with spaces so that we can say "garruk the veil cursed"
// instead of the unintuitive "garruk the veilcursed".
simpleName = simpleName.replace("-", " ");
simpleName.remove(QRegExp("[^a-zA-Z0-9 ]"));
simpleName = simpleName.toLower();
return simpleName;
} }
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info)
@ -448,6 +544,14 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info)
tmpSet=sets[i]->getShortName(); tmpSet=sets[i]->getShortName();
xml.writeAttribute("muId", QString::number(info->getMuId(tmpSet))); xml.writeAttribute("muId", QString::number(info->getMuId(tmpSet)));
tmpString = info->getCustomPicURL(tmpSet);
if(!tmpString.isEmpty())
xml.writeAttribute("picURL", tmpString);
tmpString = info->getCustomPicURLHq(tmpSet);
if(!tmpString.isEmpty())
xml.writeAttribute("picURLHq", tmpString);
xml.writeCharacters(tmpSet); xml.writeCharacters(tmpSet);
xml.writeEndElement(); xml.writeEndElement();
} }
@ -507,55 +611,55 @@ CardDatabase::~CardDatabase()
void CardDatabase::clear() void CardDatabase::clear()
{ {
QHashIterator<QString, CardSet *> setIt(setHash); QHashIterator<QString, CardSet *> setIt(sets);
while (setIt.hasNext()) { while (setIt.hasNext()) {
setIt.next(); setIt.next();
delete setIt.value(); delete setIt.value();
} }
setHash.clear(); sets.clear();
QHashIterator<QString, CardInfo *> i(cardHash); QHashIterator<QString, CardInfo *> i(cards);
while (i.hasNext()) { while (i.hasNext()) {
i.next(); i.next();
delete i.value(); delete i.value();
} }
cardHash.clear(); cards.clear();
// The pointers themselves were already deleted, so we don't delete them
// again.
simpleNameCards.clear();
} }
void CardDatabase::addCard(CardInfo *card) void CardDatabase::addCard(CardInfo *card)
{ {
cardHash.insert(card->getName(), card); cards.insert(card->getName(), card);
simpleNameCards.insert(CardInfo::simplifyName(card->getName()), card);
emit cardAdded(card); emit cardAdded(card);
} }
void CardDatabase::removeCard(CardInfo *card) void CardDatabase::removeCard(CardInfo *card)
{ {
cardHash.remove(card->getName()); cards.remove(card->getName());
simpleNameCards.remove(CardInfo::simplifyName(card->getName()));
emit cardRemoved(card); emit cardRemoved(card);
} }
CardInfo *CardDatabase::getCard(const QString &cardName, bool createIfNotFound) CardInfo *CardDatabase::getCard(const QString &cardName, bool createIfNotFound) {
{ return getCardFromMap(cards, cardName, createIfNotFound);
if (cardName.isEmpty()) }
return noCard;
else if (cardHash.contains(cardName)) CardInfo *CardDatabase::getCardBySimpleName(const QString &cardName, bool createIfNotFound) {
return cardHash.value(cardName); QString simpleName = CardInfo::simplifyName(cardName);
else if (createIfNotFound) { return getCardFromMap(simpleNameCards, simpleName, createIfNotFound);
CardInfo *newCard = new CardInfo(this, cardName, true);
newCard->addToSet(getSet("TK"));
cardHash.insert(cardName, newCard);
return newCard;
} else
return 0;
} }
CardSet *CardDatabase::getSet(const QString &setName) CardSet *CardDatabase::getSet(const QString &setName)
{ {
if (setHash.contains(setName)) if (sets.contains(setName))
return setHash.value(setName); return sets.value(setName);
else { else {
CardSet *newSet = new CardSet(setName); CardSet *newSet = new CardSet(setName);
setHash.insert(setName, newSet); sets.insert(setName, newSet);
return newSet; return newSet;
} }
} }
@ -563,7 +667,7 @@ CardSet *CardDatabase::getSet(const QString &setName)
SetList CardDatabase::getSetList() const SetList CardDatabase::getSetList() const
{ {
SetList result; SetList result;
QHashIterator<QString, CardSet *> i(setHash); QHashIterator<QString, CardSet *> i(sets);
while (i.hasNext()) { while (i.hasNext()) {
i.next(); i.next();
result << i.value(); result << i.value();
@ -573,7 +677,9 @@ SetList CardDatabase::getSetList() const
void CardDatabase::clearPixmapCache() void CardDatabase::clearPixmapCache()
{ {
QHashIterator<QString, CardInfo *> i(cardHash); // This also clears the cards in simpleNameCards since they point to the
// same object.
QHashIterator<QString, CardInfo *> i(cards);
while (i.hasNext()) { while (i.hasNext()) {
i.next(); i.next();
i.value()->clearPixmapCache(); i.value()->clearPixmapCache();
@ -597,7 +703,7 @@ void CardDatabase::loadSetsFromXml(QXmlStreamReader &xml)
else if (xml.name() == "longname") else if (xml.name() == "longname")
longName = xml.readElementText(); longName = xml.readElementText();
} }
setHash.insert(shortName, new CardSet(shortName, longName)); sets.insert(shortName, new CardSet(shortName, longName));
} }
} }
} }
@ -610,6 +716,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
if (xml.name() == "card") { if (xml.name() == "card") {
QString name, manacost, type, pt, text; QString name, manacost, type, pt, text;
QStringList colors; QStringList colors;
QStringMap customPicURLs, customPicURLsHq;
MuidMap muids; MuidMap muids;
SetList sets; SetList sets;
int tableRow = 0; int tableRow = 0;
@ -636,6 +743,12 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
if (attrs.hasAttribute("muId")) { if (attrs.hasAttribute("muId")) {
muids[setName] = attrs.value("muId").toString().toInt(); muids[setName] = attrs.value("muId").toString().toInt();
} }
if (attrs.hasAttribute("picURL")) {
customPicURLs[setName] = attrs.value("picURL").toString();
}
if (attrs.hasAttribute("picURLHq")) {
customPicURLsHq[setName] = attrs.value("picURLHq").toString();
}
} else if (xml.name() == "color") } else if (xml.name() == "color")
colors << xml.readElementText(); colors << xml.readElementText();
else if (xml.name() == "tablerow") else if (xml.name() == "tablerow")
@ -647,46 +760,32 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
else if (xml.name() == "token") else if (xml.name() == "token")
isToken = xml.readElementText().toInt(); isToken = xml.readElementText().toInt();
} }
cardHash.insert(name, new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, muids)); addCard(new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, customPicURLs, customPicURLsHq, muids));
} }
} }
} }
LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens) CardInfo *CardDatabase::getCardFromMap(CardNameMap &cardMap, const QString &cardName, bool createIfNotFound) {
if (cardName.isEmpty())
return noCard;
else if (cardMap.contains(cardName))
return cardMap.value(cardName);
else if (createIfNotFound) {
CardInfo *newCard = new CardInfo(this, cardName, true);
newCard->addToSet(getSet("TK"));
cardMap.insert(cardName, newCard);
return newCard;
} else
return 0;
}
LoadStatus CardDatabase::loadFromFile(const QString &fileName)
{ {
QFile file(fileName); QFile file(fileName);
file.open(QIODevice::ReadOnly); file.open(QIODevice::ReadOnly);
if (!file.isOpen()) if (!file.isOpen())
return FileError; return FileError;
if (tokens) {
QMutableHashIterator<QString, CardInfo *> i(cardHash);
while (i.hasNext()) {
i.next();
if (i.value()->getIsToken()) {
delete i.value();
i.remove();
}
}
} else {
QHashIterator<QString, CardSet *> setIt(setHash);
while (setIt.hasNext()) {
setIt.next();
delete setIt.value();
}
setHash.clear();
QMutableHashIterator<QString, CardInfo *> i(cardHash);
while (i.hasNext()) {
i.next();
if (!i.value()->getIsToken()) {
delete i.value();
i.remove();
}
}
cardHash.clear();
}
QXmlStreamReader xml(&file); QXmlStreamReader xml(&file);
while (!xml.atEnd()) { while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::StartElement) { if (xml.readNext() == QXmlStreamReader::StartElement) {
@ -707,9 +806,9 @@ LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens)
} }
} }
} }
qDebug() << cardHash.size() << "cards in" << setHash.size() << "sets loaded"; qDebug() << cards.size() << "cards in" << sets.size() << "sets loaded";
if (cardHash.isEmpty()) return NoCards; if (cards.isEmpty()) return NoCards;
return Ok; return Ok;
} }
@ -728,17 +827,16 @@ bool CardDatabase::saveToFile(const QString &fileName, bool tokens)
if (!tokens) { if (!tokens) {
xml.writeStartElement("sets"); xml.writeStartElement("sets");
QHashIterator<QString, CardSet *> setIterator(setHash); QHashIterator<QString, CardSet *> setIterator(sets);
while (setIterator.hasNext()) while (setIterator.hasNext())
xml << setIterator.next().value(); xml << setIterator.next().value();
xml.writeEndElement(); // sets xml.writeEndElement(); // sets
} }
xml.writeStartElement("cards"); xml.writeStartElement("cards");
QHashIterator<QString, CardInfo *> cardIterator(cardHash); QHashIterator<QString, CardInfo *> cardIterator(cards);
while (cardIterator.hasNext()) { while (cardIterator.hasNext()) {
CardInfo *card = cardIterator.next().value(); CardInfo *card = cardIterator.next().value();
if (card->getIsToken() == tokens)
xml << card; xml << card;
} }
xml.writeEndElement(); // cards xml.writeEndElement(); // cards
@ -753,7 +851,7 @@ void CardDatabase::picDownloadChanged()
{ {
pictureLoader->setPicDownload(settingsCache->getPicDownload()); pictureLoader->setPicDownload(settingsCache->getPicDownload());
if (settingsCache->getPicDownload()) { if (settingsCache->getPicDownload()) {
QHashIterator<QString, CardInfo *> cardIterator(cardHash); QHashIterator<QString, CardInfo *> cardIterator(cards);
while (cardIterator.hasNext()) while (cardIterator.hasNext())
cardIterator.next().value()->clearPixmapCacheMiss(); cardIterator.next().value()->clearPixmapCacheMiss();
} }
@ -763,7 +861,7 @@ void CardDatabase::picDownloadHqChanged()
{ {
pictureLoader->setPicDownloadHq(settingsCache->getPicDownloadHq()); pictureLoader->setPicDownloadHq(settingsCache->getPicDownloadHq());
if (settingsCache->getPicDownloadHq()) { if (settingsCache->getPicDownloadHq()) {
QHashIterator<QString, CardInfo *> cardIterator(cardHash); QHashIterator<QString, CardInfo *> cardIterator(cards);
while (cardIterator.hasNext()) while (cardIterator.hasNext())
cardIterator.next().value()->clearPixmapCacheMiss(); cardIterator.next().value()->clearPixmapCacheMiss();
} }
@ -773,11 +871,11 @@ LoadStatus CardDatabase::loadCardDatabase(const QString &path, bool tokens)
{ {
LoadStatus tempLoadStatus = NotLoaded; LoadStatus tempLoadStatus = NotLoaded;
if (!path.isEmpty()) if (!path.isEmpty())
tempLoadStatus = loadFromFile(path, tokens); tempLoadStatus = loadFromFile(path);
if (tempLoadStatus == Ok) { if (tempLoadStatus == Ok) {
SetList allSets; SetList allSets;
QHashIterator<QString, CardSet *> setsIterator(setHash); QHashIterator<QString, CardSet *> setsIterator(sets);
while (setsIterator.hasNext()) while (setsIterator.hasNext())
allSets.append(setsIterator.next().value()); allSets.append(setsIterator.next().value());
allSets.sortByKey(); allSets.sortByKey();
@ -809,7 +907,7 @@ void CardDatabase::loadTokenDatabase()
QStringList CardDatabase::getAllColors() const QStringList CardDatabase::getAllColors() const
{ {
QSet<QString> colors; QSet<QString> colors;
QHashIterator<QString, CardInfo *> cardIterator(cardHash); QHashIterator<QString, CardInfo *> cardIterator(cards);
while (cardIterator.hasNext()) { while (cardIterator.hasNext()) {
const QStringList &cardColors = cardIterator.next().value()->getColors(); const QStringList &cardColors = cardIterator.next().value()->getColors();
if (cardColors.isEmpty()) if (cardColors.isEmpty())
@ -824,7 +922,7 @@ QStringList CardDatabase::getAllColors() const
QStringList CardDatabase::getAllMainCardTypes() const QStringList CardDatabase::getAllMainCardTypes() const
{ {
QSet<QString> types; QSet<QString> types;
QHashIterator<QString, CardInfo *> cardIterator(cardHash); QHashIterator<QString, CardInfo *> cardIterator(cards);
while (cardIterator.hasNext()) while (cardIterator.hasNext())
types.insert(cardIterator.next().value()->getMainCardType()); types.insert(cardIterator.next().value()->getMainCardType());
return types.toList(); return types.toList();

View file

@ -55,7 +55,7 @@ public:
PictureToLoad(CardInfo *_card = 0, bool _stripped = false, bool _hq = true); PictureToLoad(CardInfo *_card = 0, bool _stripped = false, bool _hq = true);
CardInfo *getCard() const { return card; } CardInfo *getCard() const { return card; }
bool getStripped() const { return stripped; } bool getStripped() const { return stripped; }
QString getSetName() const { return sortedSets[setIndex]->getCorrectedShortName(); } QString getSetName() const;
bool nextSet(); bool nextSet();
bool getHq() const { return hq; } bool getHq() const { return hq; }
void setHq(bool _hq) { hq = _hq; } void setHq(bool _hq) { hq = _hq; }
@ -95,6 +95,13 @@ private:
CardDatabase *db; CardDatabase *db;
QString name; QString name;
/*
* The name without punctuation or capitalization, for better card tag name
* recognition.
*/
QString simpleName;
bool isToken; bool isToken;
SetList sets; SetList sets;
QString manacost; QString manacost;
@ -103,6 +110,7 @@ private:
QString text; QString text;
QStringList colors; QStringList colors;
int loyalty; int loyalty;
QStringMap customPicURLs, customPicURLsHq;
MuidMap muIds; MuidMap muIds;
bool cipt; bool cipt;
int tableRow; int tableRow;
@ -121,6 +129,8 @@ public:
bool _cipt = false, bool _cipt = false,
int _tableRow = 0, int _tableRow = 0,
const SetList &_sets = SetList(), const SetList &_sets = SetList(),
const QStringMap &_customPicURLs = QStringMap(),
const QStringMap &_customPicURLsHq = QStringMap(),
MuidMap muids = MuidMap()); MuidMap muids = MuidMap());
~CardInfo(); ~CardInfo();
const QString &getName() const { return name; } const QString &getName() const { return name; }
@ -138,12 +148,16 @@ public:
void setText(const QString &_text) { text = _text; emit cardInfoChanged(this); } void setText(const QString &_text) { text = _text; emit cardInfoChanged(this); }
void setColors(const QStringList &_colors) { colors = _colors; emit cardInfoChanged(this); } void setColors(const QStringList &_colors) { colors = _colors; emit cardInfoChanged(this); }
const QStringList &getColors() const { return colors; } const QStringList &getColors() const { return colors; }
QString getCustomPicURL(const QString &set) const { return customPicURLs.value(set); }
QString getCustomPicURLHq(const QString &set) const { return customPicURLsHq.value(set); }
int getMuId(const QString &set) const { return muIds.value(set); } int getMuId(const QString &set) const { return muIds.value(set); }
QString getMainCardType() const; QString getMainCardType() const;
QString getCorrectedName() const; QString getCorrectedName() const;
int getTableRow() const { return tableRow; } int getTableRow() const { return tableRow; }
void setTableRow(int _tableRow) { tableRow = _tableRow; } void setTableRow(int _tableRow) { tableRow = _tableRow; }
void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(this); } void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(this); }
void setCustomPicURL(const QString &_set, const QString &_customPicURL) { customPicURLs.insert(_set, _customPicURL); }
void setCustomPicURLHq(const QString &_set, const QString &_customPicURL) { customPicURLsHq.insert(_set, _customPicURL); }
void setMuId(const QString &_set, const int &_muId) { muIds.insert(_set, _muId); } void setMuId(const QString &_set, const int &_muId) { muIds.insert(_set, _muId); }
void addToSet(CardSet *set); void addToSet(CardSet *set);
QPixmap *loadPixmap(); QPixmap *loadPixmap();
@ -153,6 +167,12 @@ public:
void imageLoaded(const QImage &image); void imageLoaded(const QImage &image);
CardSet *getPreferredSet(); CardSet *getPreferredSet();
int getPreferredMuId(); int getPreferredMuId();
/**
* Simplify a name to have no punctuation and lowercase all letters, for
* less strict name-matching.
*/
static QString simplifyName(const QString &name);
public slots: public slots:
void updatePixmapCache(); void updatePixmapCache();
signals: signals:
@ -162,11 +182,27 @@ signals:
enum LoadStatus { Ok, VersionTooOld, Invalid, NotLoaded, FileError, NoCards }; enum LoadStatus { Ok, VersionTooOld, Invalid, NotLoaded, FileError, NoCards };
typedef QHash<QString, CardInfo *> CardNameMap;
typedef QHash<QString, CardSet *> SetNameMap;
class CardDatabase : public QObject { class CardDatabase : public QObject {
Q_OBJECT Q_OBJECT
protected: protected:
QHash<QString, CardInfo *> cardHash; /*
QHash<QString, CardSet *> setHash; * The cards, indexed by name.
*/
CardNameMap cards;
/**
* The cards, indexed by their simple name.
*/
CardNameMap simpleNameCards;
/*
* The sets, indexed by short name.
*/
SetNameMap sets;
CardInfo *noCard; CardInfo *noCard;
QThread *pictureLoaderThread; QThread *pictureLoaderThread;
@ -176,6 +212,8 @@ private:
static const int versionNeeded; static const int versionNeeded;
void loadCardsFromXml(QXmlStreamReader &xml); void loadCardsFromXml(QXmlStreamReader &xml);
void loadSetsFromXml(QXmlStreamReader &xml); void loadSetsFromXml(QXmlStreamReader &xml);
CardInfo *getCardFromMap(CardNameMap &cardMap, const QString &cardName, bool createIfNotFound);
public: public:
CardDatabase(QObject *parent = 0); CardDatabase(QObject *parent = 0);
~CardDatabase(); ~CardDatabase();
@ -183,10 +221,17 @@ public:
void addCard(CardInfo *card); void addCard(CardInfo *card);
void removeCard(CardInfo *card); void removeCard(CardInfo *card);
CardInfo *getCard(const QString &cardName = QString(), bool createIfNotFound = true); CardInfo *getCard(const QString &cardName = QString(), bool createIfNotFound = true);
/*
* 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 = QString(), bool createIfNotFound = true);
CardSet *getSet(const QString &setName); CardSet *getSet(const QString &setName);
QList<CardInfo *> getCardList() const { return cardHash.values(); } QList<CardInfo *> getCardList() const { return cards.values(); }
SetList getSetList() const; SetList getSetList() const;
LoadStatus loadFromFile(const QString &fileName, bool tokens = false); LoadStatus loadFromFile(const QString &fileName);
bool saveToFile(const QString &fileName, bool tokens = false); bool saveToFile(const QString &fileName, bool tokens = false);
QStringList getAllColors() const; QStringList getAllColors() const;
QStringList getAllMainCardTypes() const; QStringList getAllMainCardTypes() const;

View file

@ -81,7 +81,7 @@ CardInfoWidget::CardInfoWidget(ResizeMode _mode, const QString &cardName, QWidge
} else } else
setFixedWidth(250); setFixedWidth(250);
setCard(db->getCard(cardName)); setCard(getCard(cardName));
setMinimized(settingsCache->getCardInfoMinimized()); setMinimized(settingsCache->getCardInfoMinimized());
} }
@ -166,7 +166,7 @@ void CardInfoWidget::setCard(CardInfo *card)
void CardInfoWidget::setCard(const QString &cardName) void CardInfoWidget::setCard(const QString &cardName)
{ {
setCard(db->getCard(cardName)); setCard(getCard(cardName));
} }
void CardInfoWidget::setCard(AbstractCardItem *card) void CardInfoWidget::setCard(AbstractCardItem *card)
@ -176,7 +176,11 @@ void CardInfoWidget::setCard(AbstractCardItem *card)
void CardInfoWidget::clear() void CardInfoWidget::clear()
{ {
setCard(db->getCard()); setCard(getCard());
}
CardInfo *CardInfoWidget::getCard(const QString &cardName) {
return db->getCardBySimpleName(cardName);
} }
void CardInfoWidget::updatePixmap() void CardInfoWidget::updatePixmap()
@ -188,7 +192,7 @@ void CardInfoWidget::updatePixmap()
if (resizedPixmap) if (resizedPixmap)
cardPicture->setPixmap(*resizedPixmap); cardPicture->setPixmap(*resizedPixmap);
else else
cardPicture->setPixmap(*(db->getCard()->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio)))); cardPicture->setPixmap(*(getCard()->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio))));
} }
void CardInfoWidget::retranslateUi() void CardInfoWidget::retranslateUi()

View file

@ -42,6 +42,11 @@ private:
CardInfo *info; CardInfo *info;
void setMinimized(int _minimized); void setMinimized(int _minimized);
/*
* Wrapper around db->getCardBySimpleName.
*/
CardInfo *getCard(const QString &cardName = QString());
public: public:
CardInfoWidget(ResizeMode _mode, const QString &cardName = QString(), QWidget *parent = 0, Qt::WindowFlags f = 0); CardInfoWidget(ResizeMode _mode, const QString &cardName = QString(), QWidget *parent = 0, Qt::WindowFlags f = 0);
void retranslateUi(); void retranslateUi();

View file

@ -233,6 +233,7 @@ CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPoin
void CardItem::deleteDragItem() void CardItem::deleteDragItem()
{ {
if(dragItem)
dragItem->deleteLater(); dragItem->deleteLater();
dragItem = NULL; dragItem = NULL;
} }

View file

@ -472,13 +472,7 @@ void DeckListModel::printDeckList(QPrinter *printer)
doc.print(printer); doc.print(printer);
} }
void DeckListModel::pricesUpdated(InnerDecklistNode *node) void DeckListModel::pricesUpdated()
{ {
if (!node) emit layoutChanged();
node = root;
if (node->isEmpty())
return;
emit dataChanged(createIndex(0, 2, node->at(0)), createIndex(node->size() - 1, 2, node->last()));
} }

View file

@ -51,7 +51,7 @@ public:
void cleanList(); void cleanList();
DeckLoader *getDeckList() const { return deckList; } DeckLoader *getDeckList() const { return deckList; }
void setDeckList(DeckLoader *_deck); void setDeckList(DeckLoader *_deck);
void pricesUpdated(InnerDecklistNode *node = 0); void pricesUpdated();
private: private:
DeckLoader *deckList; DeckLoader *deckList;
InnerDecklistNode *root; InnerDecklistNode *root;

View file

@ -11,8 +11,10 @@
#include <QUrlQuery> #include <QUrlQuery>
#endif #endif
DeckStatsInterface::DeckStatsInterface(QObject *parent) DeckStatsInterface::DeckStatsInterface(
: QObject(parent) CardDatabase &_cardDatabase,
QObject *parent
) : QObject(parent), cardDatabase(_cardDatabase)
{ {
manager = new QNetworkAccessManager(this); manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(queryFinished(QNetworkReply *))); connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(queryFinished(QNetworkReply *)));
@ -43,24 +45,23 @@ void DeckStatsInterface::queryFinished(QNetworkReply *reply)
deleteLater(); deleteLater();
} }
void DeckStatsInterface::getAnalyzeRequestData(DeckList *deck, QByteArray *data)
{
DeckList deckWithoutTokens;
copyDeckWithoutTokens(*deck, deckWithoutTokens);
#if QT_VERSION < 0x050000 #if QT_VERSION < 0x050000
void DeckStatsInterface::getAnalyzeRequestData(DeckList *deck, QByteArray *data)
{
QUrl params; QUrl params;
params.addQueryItem("deck", deck->writeToString_Plain()); params.addQueryItem("deck", deckWithoutTokens.writeToString_Plain());
data->append(params.encodedQuery()); data->append(params.encodedQuery());
}
#else #else
void DeckStatsInterface::getAnalyzeRequestData(DeckList *deck, QByteArray *data)
{
QUrl params; QUrl params;
QUrlQuery urlQuery; QUrlQuery urlQuery;
urlQuery.addQueryItem("deck", deck->writeToString_Plain()); urlQuery.addQueryItem("deck", deckWithoutTokens.writeToString_Plain());
params.setQuery(urlQuery); params.setQuery(urlQuery);
data->append(params.query(QUrl::EncodeReserved)); data->append(params.query(QUrl::EncodeReserved));
}
#endif #endif
}
void DeckStatsInterface::analyzeDeck(DeckList *deck) void DeckStatsInterface::analyzeDeck(DeckList *deck)
{ {
@ -72,3 +73,34 @@ void DeckStatsInterface::analyzeDeck(DeckList *deck)
manager->post(request, data); manager->post(request, data);
} }
struct CopyIfNotAToken {
CardDatabase &cardDatabase;
DeckList &destination;
CopyIfNotAToken(
CardDatabase &_cardDatabase,
DeckList &_destination
) : cardDatabase(_cardDatabase), destination(_destination) {};
void operator()(
const InnerDecklistNode *node,
const DecklistCardNode *card
) const {
if (!cardDatabase.getCard(card->getName())->getIsToken()) {
DecklistCardNode *addedCard = destination.addCard(
card->getName(),
node->getName()
);
addedCard->setNumber(card->getNumber());
}
}
};
void DeckStatsInterface::copyDeckWithoutTokens(
const DeckList &source,
DeckList &destination
) {
CopyIfNotAToken copyIfNotAToken(cardDatabase, destination);
source.forEachCard(copyIfNotAToken);
}

View file

@ -1,6 +1,8 @@
#ifndef DECKSTATS_INTERFACE_H #ifndef DECKSTATS_INTERFACE_H
#define DECKSTATS_INTERFACE_H #define DECKSTATS_INTERFACE_H
#include "carddatabase.h"
#include "decklist.h"
#include <QObject> #include <QObject>
class QByteArray; class QByteArray;
@ -12,11 +14,21 @@ class DeckStatsInterface : public QObject {
Q_OBJECT Q_OBJECT
private: private:
QNetworkAccessManager *manager; QNetworkAccessManager *manager;
CardDatabase &cardDatabase;
/**
* Deckstats doesn't recognize token cards, and instead tries to find the
* closest non-token card instead. So we construct a new deck which has no
* tokens.
*/
void copyDeckWithoutTokens(const DeckList &source, DeckList& destination);
private slots: private slots:
void queryFinished(QNetworkReply *reply); void queryFinished(QNetworkReply *reply);
void getAnalyzeRequestData(DeckList *deck, QByteArray *data); void getAnalyzeRequestData(DeckList *deck, QByteArray *data);
public: public:
DeckStatsInterface(QObject *parent = 0); DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent = 0);
void analyzeDeck(DeckList *deck); void analyzeDeck(DeckList *deck);
}; };

View file

@ -113,7 +113,7 @@ DlgEditTokens::DlgEditTokens(CardDatabaseModel *_cardDatabaseModel, QWidget *par
setWindowTitle(tr("Edit tokens")); setWindowTitle(tr("Edit tokens"));
} }
void DlgEditTokens::tokenSelectionChanged(const QModelIndex &current, const QModelIndex &previous) void DlgEditTokens::tokenSelectionChanged(const QModelIndex &current, const QModelIndex & /* previous */)
{ {
const QModelIndex realIndex = cardDatabaseDisplayModel->mapToSource(current); const QModelIndex realIndex = cardDatabaseDisplayModel->mapToSource(current);
CardInfo *cardInfo = current.row() >= 0 ? cardDatabaseModel->getCard(realIndex.row()) : cardDatabaseModel->getDatabase()->getCard(); CardInfo *cardInfo = current.row() >= 0 ? cardDatabaseModel->getCard(realIndex.row()) : cardDatabaseModel->getDatabase()->getCard();

View file

@ -9,26 +9,53 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QGridLayout> #include <QGridLayout>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QSettings>
#include <QCryptographicHash>
DlgFilterGames::DlgFilterGames(const QMap<int, QString> &allGameTypes, QWidget *parent) DlgFilterGames::DlgFilterGames(const QMap<int, QString> &_allGameTypes, QWidget *parent)
: QDialog(parent) : QDialog(parent),
allGameTypes(_allGameTypes)
{ {
unavailableGamesVisibleCheckBox = new QCheckBox(tr("Show &unavailable games")); QSettings settings;
passwordProtectedGamesVisibleCheckBox = new QCheckBox(tr("Show &password protected games")); settings.beginGroup("filter_games");
unavailableGamesVisibleCheckBox = new QCheckBox(tr("Show &unavailable games"));
unavailableGamesVisibleCheckBox->setChecked(
settings.value("unavailable_games_visible", false).toBool()
);
passwordProtectedGamesVisibleCheckBox = new QCheckBox(tr("Show &password protected games"));
passwordProtectedGamesVisibleCheckBox->setChecked(
settings.value("password_protected_games_visible", false).toBool()
);
QLabel *gameNameFilterLabel = new QLabel(tr("Game &description:"));
gameNameFilterEdit = new QLineEdit; gameNameFilterEdit = new QLineEdit;
gameNameFilterEdit->setText(
settings.value("game_name_filter", "").toString()
);
QLabel *gameNameFilterLabel = new QLabel(tr("Game &description:"));
gameNameFilterLabel->setBuddy(gameNameFilterEdit); gameNameFilterLabel->setBuddy(gameNameFilterEdit);
QLabel *creatorNameFilterLabel = new QLabel(tr("&Creator name:"));
creatorNameFilterEdit = new QLineEdit; creatorNameFilterEdit = new QLineEdit;
creatorNameFilterEdit->setText(
settings.value("creator_name_filter", "").toString()
);
QLabel *creatorNameFilterLabel = new QLabel(tr("&Creator name:"));
creatorNameFilterLabel->setBuddy(creatorNameFilterEdit); creatorNameFilterLabel->setBuddy(creatorNameFilterEdit);
QVBoxLayout *gameTypeFilterLayout = new QVBoxLayout; QVBoxLayout *gameTypeFilterLayout = new QVBoxLayout;
QMapIterator<int, QString> gameTypesIterator(allGameTypes); QMapIterator<int, QString> gameTypesIterator(allGameTypes);
while (gameTypesIterator.hasNext()) { while (gameTypesIterator.hasNext()) {
gameTypesIterator.next(); gameTypesIterator.next();
QCheckBox *temp = new QCheckBox(gameTypesIterator.value()); QCheckBox *temp = new QCheckBox(gameTypesIterator.value());
temp->setChecked(
settings.value(
"game_type/" + hashGameType(gameTypesIterator.value()),
false
).toBool()
);
gameTypeFilterCheckBoxes.insert(gameTypesIterator.key(), temp); gameTypeFilterCheckBoxes.insert(gameTypesIterator.key(), temp);
gameTypeFilterLayout->addWidget(temp); gameTypeFilterLayout->addWidget(temp);
} }
@ -43,14 +70,18 @@ DlgFilterGames::DlgFilterGames(const QMap<int, QString> &allGameTypes, QWidget *
maxPlayersFilterMinSpinBox = new QSpinBox; maxPlayersFilterMinSpinBox = new QSpinBox;
maxPlayersFilterMinSpinBox->setMinimum(1); maxPlayersFilterMinSpinBox->setMinimum(1);
maxPlayersFilterMinSpinBox->setMaximum(99); maxPlayersFilterMinSpinBox->setMaximum(99);
maxPlayersFilterMinSpinBox->setValue(1); maxPlayersFilterMinSpinBox->setValue(
settings.value("min_players", 1).toInt()
);
maxPlayersFilterMinLabel->setBuddy(maxPlayersFilterMinSpinBox); maxPlayersFilterMinLabel->setBuddy(maxPlayersFilterMinSpinBox);
QLabel *maxPlayersFilterMaxLabel = new QLabel(tr("at &most:")); QLabel *maxPlayersFilterMaxLabel = new QLabel(tr("at &most:"));
maxPlayersFilterMaxSpinBox = new QSpinBox; maxPlayersFilterMaxSpinBox = new QSpinBox;
maxPlayersFilterMaxSpinBox->setMinimum(1); maxPlayersFilterMaxSpinBox->setMinimum(1);
maxPlayersFilterMaxSpinBox->setMaximum(99); maxPlayersFilterMaxSpinBox->setMaximum(99);
maxPlayersFilterMaxSpinBox->setValue(99); maxPlayersFilterMaxSpinBox->setValue(
settings.value("max_players", 99).toInt()
);
maxPlayersFilterMaxLabel->setBuddy(maxPlayersFilterMaxSpinBox); maxPlayersFilterMaxLabel->setBuddy(maxPlayersFilterMaxSpinBox);
QGridLayout *maxPlayersFilterLayout = new QGridLayout; QGridLayout *maxPlayersFilterLayout = new QGridLayout;
@ -83,7 +114,7 @@ DlgFilterGames::DlgFilterGames(const QMap<int, QString> &allGameTypes, QWidget *
hbox->addLayout(rightColumn); hbox->addLayout(rightColumn);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
QVBoxLayout *mainLayout = new QVBoxLayout; QVBoxLayout *mainLayout = new QVBoxLayout;
@ -94,6 +125,42 @@ DlgFilterGames::DlgFilterGames(const QMap<int, QString> &allGameTypes, QWidget *
setWindowTitle(tr("Filter games")); setWindowTitle(tr("Filter games"));
} }
void DlgFilterGames::actOk() {
QSettings settings;
settings.beginGroup("filter_games");
settings.setValue(
"unavailable_games_visible",
unavailableGamesVisibleCheckBox->isChecked()
);
settings.setValue(
"password_protected_games_visible",
passwordProtectedGamesVisibleCheckBox->isChecked()
);
settings.setValue("game_name_filter", gameNameFilterEdit->text());
settings.setValue("creator_name_filter", creatorNameFilterEdit->text());
QMapIterator<int, QString> gameTypeIterator(allGameTypes);
QMapIterator<int, QCheckBox *> checkboxIterator(gameTypeFilterCheckBoxes);
while (gameTypeIterator.hasNext()) {
gameTypeIterator.next();
checkboxIterator.next();
settings.setValue(
"game_type/" + hashGameType(gameTypeIterator.value()),
checkboxIterator.value()->isChecked()
);
}
settings.setValue("min_players", maxPlayersFilterMinSpinBox->value());
settings.setValue("max_players", maxPlayersFilterMaxSpinBox->value());
accept();
}
QString DlgFilterGames::hashGameType(const QString &gameType) const {
return QCryptographicHash::hash(gameType.toUtf8(), QCryptographicHash::Md5).toHex();
}
bool DlgFilterGames::getUnavailableGamesVisible() const bool DlgFilterGames::getUnavailableGamesVisible() const
{ {
return unavailableGamesVisibleCheckBox->isChecked(); return unavailableGamesVisibleCheckBox->isChecked();

View file

@ -19,6 +19,16 @@ private:
QMap<int, QCheckBox *> gameTypeFilterCheckBoxes; QMap<int, QCheckBox *> gameTypeFilterCheckBoxes;
QSpinBox *maxPlayersFilterMinSpinBox; QSpinBox *maxPlayersFilterMinSpinBox;
QSpinBox *maxPlayersFilterMaxSpinBox; QSpinBox *maxPlayersFilterMaxSpinBox;
const QMap<int, QString> &allGameTypes;
/*
* The game type might contain special characters, so to use it in
* QSettings we just hash it.
*/
QString hashGameType(const QString &gameType) const;
private slots:
void actOk();
public: public:
DlgFilterGames(const QMap<int, QString> &allGameTypes, QWidget *parent = 0); DlgFilterGames(const QMap<int, QString> &allGameTypes, QWidget *parent = 0);

View file

@ -18,11 +18,13 @@
#include <QInputDialog> #include <QInputDialog>
#include <QSpinBox> #include <QSpinBox>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QRadioButton>
#include <QDebug> #include <QDebug>
#include "carddatabase.h" #include "carddatabase.h"
#include "dlg_settings.h" #include "dlg_settings.h"
#include "main.h" #include "main.h"
#include "settingscache.h" #include "settingscache.h"
#include "priceupdater.h"
GeneralSettingsPage::GeneralSettingsPage() GeneralSettingsPage::GeneralSettingsPage()
{ {
@ -532,8 +534,29 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
priceTagsCheckBox->setChecked(settingsCache->getPriceTagFeature()); priceTagsCheckBox->setChecked(settingsCache->getPriceTagFeature());
connect(priceTagsCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPriceTagFeature(int))); connect(priceTagsCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPriceTagFeature(int)));
priceTagSource0 = new QRadioButton;
priceTagSource1 = new QRadioButton;
switch(settingsCache->getPriceTagSource())
{
case AbstractPriceUpdater::DBPriceSource:
priceTagSource1->setChecked(true);
break;
case AbstractPriceUpdater::BLPPriceSource:
default:
priceTagSource0->setChecked(true);
break;
}
connect(priceTagSource0, SIGNAL(toggled(bool)), this, SLOT(radioPriceTagSourceClicked(bool)));
connect(priceTagSource1, SIGNAL(toggled(bool)), this, SLOT(radioPriceTagSourceClicked(bool)));
connect(this, SIGNAL(priceTagSourceChanged(int)), settingsCache, SLOT(setPriceTagSource(int)));
QGridLayout *generalGrid = new QGridLayout; QGridLayout *generalGrid = new QGridLayout;
generalGrid->addWidget(priceTagsCheckBox, 0, 0); generalGrid->addWidget(priceTagsCheckBox, 0, 0);
generalGrid->addWidget(priceTagSource0, 1, 0);
generalGrid->addWidget(priceTagSource1, 2, 0);
generalGroupBox = new QGroupBox; generalGroupBox = new QGroupBox;
generalGroupBox->setLayout(generalGrid); generalGroupBox->setLayout(generalGrid);
@ -546,10 +569,26 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
void DeckEditorSettingsPage::retranslateUi() void DeckEditorSettingsPage::retranslateUi()
{ {
priceTagsCheckBox->setText(tr("Enable &price tag feature (using data from blacklotusproject.com)")); priceTagsCheckBox->setText(tr("Enable &price tag feature"));
priceTagSource0->setText(tr("using data from blacklotusproject.com"));
priceTagSource1->setText(tr("using data from deckbrew.com"));
generalGroupBox->setTitle(tr("General")); generalGroupBox->setTitle(tr("General"));
} }
void DeckEditorSettingsPage::radioPriceTagSourceClicked(bool checked)
{
if(!checked)
return;
int source=AbstractPriceUpdater::BLPPriceSource;
if(priceTagSource0->isChecked())
source=AbstractPriceUpdater::BLPPriceSource;
if(priceTagSource1->isChecked())
source=AbstractPriceUpdater::DBPriceSource;
emit priceTagSourceChanged(source);
}
MessagesSettingsPage::MessagesSettingsPage() MessagesSettingsPage::MessagesSettingsPage()
{ {
aAdd = new QAction(this); aAdd = new QAction(this);

View file

@ -15,6 +15,7 @@ class QCheckBox;
class QLabel; class QLabel;
class QCloseEvent; class QCloseEvent;
class QSpinBox; class QSpinBox;
class QRadioButton;
class AbstractSettingsPage : public QWidget { class AbstractSettingsPage : public QWidget {
public: public:
@ -100,8 +101,13 @@ class DeckEditorSettingsPage : public AbstractSettingsPage {
public: public:
DeckEditorSettingsPage(); DeckEditorSettingsPage();
void retranslateUi(); void retranslateUi();
private slots:
void radioPriceTagSourceClicked(bool checked);
signals:
void priceTagSourceChanged(int _priceTagSource);
private: private:
QCheckBox *priceTagsCheckBox; QCheckBox *priceTagsCheckBox;
QRadioButton *priceTagSource0, *priceTagSource1;
QGroupBox *generalGroupBox; QGroupBox *generalGroupBox;
}; };

View file

@ -18,10 +18,10 @@ public:
virtual void enable() { enabled = true; nodeChanged(); } virtual void enable() { enabled = true; nodeChanged(); }
virtual void disable() { enabled = false; nodeChanged(); } virtual void disable() { enabled = false; nodeChanged(); }
virtual FilterTreeNode *parent() const { return NULL; } virtual FilterTreeNode *parent() const { return NULL; }
virtual FilterTreeNode *nodeAt(int i) const { return NULL; } virtual FilterTreeNode *nodeAt(int /* i */) const { return NULL; }
virtual void deleteAt(int i) {} virtual void deleteAt(int /* i */) {}
virtual int childCount() const { return 0; } virtual int childCount() const { return 0; }
virtual int childIndex(const FilterTreeNode *node) const { return -1; } virtual int childIndex(const FilterTreeNode * /* node */) const { return -1; }
virtual int index() const { return (parent() != NULL)? parent()->childIndex(this) : -1; } virtual int index() const { return (parent() != NULL)? parent()->childIndex(this) : -1; }
virtual QString text() const { return QString(textCStr()); } virtual QString text() const { return QString(textCStr()); }
virtual bool isLeaf() const { return false; } virtual bool isLeaf() const { return false; }
@ -48,7 +48,7 @@ class FilterTreeBranch : public FilterTreeNode {
protected: protected:
QList<T> childNodes; QList<T> childNodes;
public: public:
~FilterTreeBranch(); virtual ~FilterTreeBranch();
FilterTreeNode *nodeAt(int i) const; FilterTreeNode *nodeAt(int i) const;
void deleteAt(int i); void deleteAt(int i);
int childCount() const { return childNodes.size(); } int childCount() const { return childNodes.size(); }
@ -102,6 +102,7 @@ public:
FilterItem(QString trm, FilterItemList *parent) FilterItem(QString trm, FilterItemList *parent)
: p(parent), term(trm) {} : p(parent), term(trm) {}
virtual ~FilterItem() {};
CardFilter::Attr attr() const { return p->attr(); } CardFilter::Attr attr() const { return p->attr(); }
CardFilter::Type type() const { return p->type; } CardFilter::Type type() const { return p->type; }

View file

@ -84,12 +84,6 @@ void GameSelector::actSetFilter()
if (room) if (room)
gameTypeMap = gameListModel->getGameTypes().value(room->getRoomId()); gameTypeMap = gameListModel->getGameTypes().value(room->getRoomId());
DlgFilterGames dlg(gameTypeMap, this); DlgFilterGames dlg(gameTypeMap, this);
dlg.setUnavailableGamesVisible(gameListProxyModel->getUnavailableGamesVisible());
dlg.setPasswordProtectedGamesVisible(gameListProxyModel->getPasswordProtectedGamesVisible());
dlg.setGameNameFilter(gameListProxyModel->getGameNameFilter());
dlg.setCreatorNameFilter(gameListProxyModel->getCreatorNameFilter());
dlg.setGameTypeFilter(gameListProxyModel->getGameTypeFilter());
dlg.setMaxPlayersFilter(gameListProxyModel->getMaxPlayersFilterMin(), gameListProxyModel->getMaxPlayersFilterMax());
if (!dlg.exec()) if (!dlg.exec())
return; return;

View file

@ -33,7 +33,7 @@ ServerInfo_User LocalServer_DatabaseInterface::getUserData(const QString &name,
return result; return result;
} }
AuthenticationResult LocalServer_DatabaseInterface::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft) AuthenticationResult LocalServer_DatabaseInterface::checkUserPassword(Server_ProtocolHandler * /* handler */, const QString & /* user */, const QString & /* password */, QString & /* reasonStr */, int & /* secondsLeft */)
{ {
return UnknownUser; return UnknownUser;
} }

View file

@ -30,6 +30,7 @@
#include <QIcon> #include <QIcon>
#include <QDir> #include <QDir>
#include <QDesktopServices> #include <QDesktopServices>
#include <QDebug>
#include "main.h" #include "main.h"
#include "window_main.h" #include "window_main.h"
@ -158,6 +159,8 @@ int main(int argc, char *argv[])
QDir().mkpath(dataDir + "/pics"); QDir().mkpath(dataDir + "/pics");
settingsCache->setPicsPath(dataDir + "/pics"); settingsCache->setPicsPath(dataDir + "/pics");
} }
if (!QDir().mkpath(settingsCache->getPicsPath() + "/CUSTOM"))
qDebug() << "Could not create " + settingsCache->getPicsPath().toUtf8() + "/CUSTOM. Will fall back on default card images.";
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
if(settingsCache->getHandBgPath().isEmpty() && if(settingsCache->getHandBgPath().isEmpty() &&

View file

@ -959,6 +959,10 @@ void Player::actCreateToken()
return; return;
lastTokenName = dlg.getName(); lastTokenName = dlg.getName();
if (CardInfo *correctedCard = db->getCardBySimpleName(lastTokenName, false)) {
lastTokenName = correctedCard->getName();
}
lastTokenColor = dlg.getColor(); lastTokenColor = dlg.getColor();
lastTokenPT = dlg.getPT(); lastTokenPT = dlg.getPT();
lastTokenAnnotation = dlg.getAnnotation(); lastTokenAnnotation = dlg.getAnnotation();

View file

@ -178,5 +178,5 @@ void PlayerListWidget::showContextMenu(const QPoint &pos, const QModelIndex &ind
int playerId = index.sibling(index.row(), 4).data(Qt::UserRole + 1).toInt(); int playerId = index.sibling(index.row(), 4).data(Qt::UserRole + 1).toInt();
UserLevelFlags userLevel(index.sibling(index.row(), 3).data(Qt::UserRole).toInt()); UserLevelFlags userLevel(index.sibling(index.row(), 3).data(Qt::UserRole).toInt());
userContextMenu->showContextMenu(pos, userName, userLevel, playerId); userContextMenu->showContextMenu(pos, userName, userLevel, true, playerId);
} }

View file

@ -4,25 +4,46 @@
*/ */
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
#include <QMessageBox>
#include "qt-json/json.h" #include "qt-json/json.h"
#include "priceupdater.h" #include "priceupdater.h"
#include "main.h"
#include "carddatabase.h"
#if QT_VERSION < 0x050000
// for Qt::escape()
#include <QtGui/qtextdocument.h>
#endif
/** /**
* Constructor. * Constructor.
* *
* @param _deck deck. * @param _deck deck.
*/ */
PriceUpdater::PriceUpdater(const DeckList *_deck) AbstractPriceUpdater::AbstractPriceUpdater(const DeckList *_deck)
{ {
nam = new QNetworkAccessManager(this); nam = new QNetworkAccessManager(this);
deck = _deck; deck = _deck;
} }
// blacklotusproject.com
/**
* Constructor.
*
* @param _deck deck.
*/
BLPPriceUpdater::BLPPriceUpdater(const DeckList *_deck)
: AbstractPriceUpdater(_deck)
{
}
/** /**
* Update the prices of the cards in deckList. * Update the prices of the cards in deckList.
*/ */
void PriceUpdater::updatePrices() void BLPPriceUpdater::updatePrices()
{ {
QString q = "http://blacklotusproject.com/json/?cards="; QString q = "http://blacklotusproject.com/json/?cards=";
QStringList cards = deck->getCardList(); QStringList cards = deck->getCardList();
@ -38,7 +59,7 @@ void PriceUpdater::updatePrices()
/** /**
* Called when the download of the json file with the prices is finished. * Called when the download of the json file with the prices is finished.
*/ */
void PriceUpdater::downloadFinished() void BLPPriceUpdater::downloadFinished()
{ {
QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
bool ok; bool ok;
@ -82,3 +103,188 @@ void PriceUpdater::downloadFinished()
deleteLater(); deleteLater();
emit finishedUpdate(); emit finishedUpdate();
} }
// deckbrew.com
/**
* Constructor.
*
* @param _deck deck.
*/
DBPriceUpdater::DBPriceUpdater(const DeckList *_deck)
: AbstractPriceUpdater(_deck)
{
}
/**
* Update the prices of the cards in deckList.
*/
void DBPriceUpdater::updatePrices()
{
QString base = "https://api.deckbrew.com/mtg/cards", q = "";
QStringList cards = deck->getCardList();
muidMap.clear();
urls.clear();
CardInfo * card;
int muid;
SetList sets;
bool bNotFirst=false;
for (int i = 0; i < cards.size(); ++i) {
card = db->getCard(cards[i], false);
sets = card->getSets();
for(int j = 0; j < sets.size(); ++j)
{
muid=card->getMuId(sets[j]->getShortName());
if (!muid) {
continue;
}
//qDebug() << "muid " << muid << " card: " << cards[i] << endl;
if(bNotFirst)
{
q += QString("&m=%1").arg(muid);
} else {
q += QString("?m=%1").arg(muid);
bNotFirst = true;
}
muidMap.insert(muid, cards[i]);
if(q.length() > 240)
{
urls.append(base + q);
bNotFirst=false;
q = "";
}
}
}
if(q.length() > 0)
urls.append(base + q);
requestNext();
}
void DBPriceUpdater::requestNext()
{
if(urls.empty())
return;
QUrl url(urls.takeFirst(), QUrl::TolerantMode);
//qDebug() << "request prices from: " << url.toString() << endl;
QNetworkReply *reply = nam->get(QNetworkRequest(url));
connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished()));
}
/**
* Called when the download of the json file with the prices is finished.
*/
void DBPriceUpdater::downloadFinished()
{
QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
bool ok;
QString tmp = QString(reply->readAll());
// Errors are incapsulated in an object, check for them first
QVariantMap resultMap = QtJson::Json::parse(tmp, ok).toMap();
if (!ok) {
QMessageBox::critical(this, tr("Error"), tr("A problem has occured while fetching card prices."));
reply->deleteLater();
if(urls.isEmpty())
{
deleteLater();
emit finishedUpdate();
} else {
requestNext();
}
}
if(resultMap.contains("errors"))
{
QMessageBox::critical(this, tr("Error"), tr("A problem has occured while fetching card prices:") +
"<br/>" +
#if QT_VERSION < 0x050000
Qt::escape(resultMap["errors"].toList().first().toString())
#else
resultMap["errors"].toList().first().toString().toHtmlEscaped()
#endif
);
reply->deleteLater();
if(urls.isEmpty())
{
deleteLater();
emit finishedUpdate();
} else {
requestNext();
}
}
// Good results are a list
QVariantList resultList = QtJson::Json::parse(tmp, ok).toList();
if (!ok) {
QMessageBox::critical(this, tr("Error"), tr("A problem has occured while fetching card prices."));
reply->deleteLater();
if(urls.isEmpty())
{
deleteLater();
emit finishedUpdate();
} else {
requestNext();
}
}
QMap<QString, float> cardsPrice;
QListIterator<QVariant> it(resultList);
while (it.hasNext()) {
QVariantMap cardMap = it.next().toMap();
// get sets list
QList<QVariant> editions = cardMap.value("editions").toList();
foreach (QVariant ed, editions)
{
// retrieve card name "as we know it" from the muid
QVariantMap edition = ed.toMap();
QString set = edition.value("set_id").toString();
int muid = edition.value("multiverse_id").toString().toInt();
if(!muidMap.contains(muid))
continue;
QString name=muidMap.value(muid);
// Prices are in USD cents
float price = edition.value("price").toMap().value("median").toString().toFloat() / 100;
//qDebug() << "card " << name << " set " << set << " price " << price << endl;
/**
* Make sure Masters Edition (MED) isn't the set, as it doesn't
* physically exist. Also check the price to see that the cheapest set
* ends up as the final price.
*/
if (set != "MED" && price > 0 && (!cardsPrice.contains(name) || cardsPrice.value(name) > price))
cardsPrice.insert(name, price);
}
}
InnerDecklistNode *listRoot = deck->getRoot();
for (int i = 0; i < listRoot->size(); i++) {
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
for (int j = 0; j < currentZone->size(); j++) {
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
if (!currentCard)
continue;
float price = cardsPrice[currentCard->getName()];
if(price > 0)
currentCard->setPrice(price);
}
}
reply->deleteLater();
if(urls.isEmpty())
{
deleteLater();
emit finishedUpdate();
} else {
requestNext();
}
}

View file

@ -1,28 +1,57 @@
#ifndef PRICEUPDATER_H #ifndef PRICEUPDATER_H
#define PRICEUPDATER_H #define PRICEUPDATER_H
#include <QNetworkReply> #include <QNetworkAccessManager>
#include "decklist.h" #include "decklist.h"
class QNetworkAccessManager; class QNetworkAccessManager;
// If we don't typedef this, won't compile on OS X < 10.9
typedef QMap<int, QString> MuidStringMap;
/** /**
* Price Updater. * Price Updater.
* *
* @author Marcio Ribeiro <mmr@b1n.org> * @author Marcio Ribeiro <mmr@b1n.org>
*/ */
class PriceUpdater : public QObject class AbstractPriceUpdater : public QWidget
{ {
Q_OBJECT Q_OBJECT
private: public:
enum PriceSource { BLPPriceSource, DBPriceSource };
protected:
const DeckList *deck; const DeckList *deck;
QNetworkAccessManager *nam; QNetworkAccessManager *nam;
signals: signals:
void finishedUpdate(); void finishedUpdate();
private slots: protected slots:
void downloadFinished(); virtual void downloadFinished() = 0;
public: public:
PriceUpdater(const DeckList *deck); AbstractPriceUpdater(const DeckList *deck);
void updatePrices(); virtual void updatePrices() = 0;
};
class BLPPriceUpdater : public AbstractPriceUpdater
{
Q_OBJECT
protected:
virtual void downloadFinished();
public:
BLPPriceUpdater(const DeckList *deck);
virtual void updatePrices();
};
class DBPriceUpdater : public AbstractPriceUpdater
{
Q_OBJECT
protected:
MuidStringMap muidMap;
QList<QString> urls;
protected:
virtual void downloadFinished();
void requestNext();
public:
DBPriceUpdater(const DeckList *deck);
virtual void updatePrices();
}; };
#endif #endif

View file

@ -43,7 +43,7 @@ void ReplayTimelineWidget::setTimeline(const QList<int> &_replayTimeline)
update(); update();
} }
void ReplayTimelineWidget::paintEvent(QPaintEvent *event) void ReplayTimelineWidget::paintEvent(QPaintEvent * /* event */)
{ {
QPainter painter(this); QPainter painter(this);
painter.drawRect(0, 0, width() - 1, height() - 1); painter.drawRect(0, 0, width() - 1, height() - 1);

View file

@ -45,6 +45,7 @@ SettingsCache::SettingsCache()
soundPath = settings->value("sound/path").toString(); soundPath = settings->value("sound/path").toString();
priceTagFeature = settings->value("deckeditor/pricetags", false).toBool(); priceTagFeature = settings->value("deckeditor/pricetags", false).toBool();
priceTagSource = settings->value("deckeditor/pricetagsource", 0).toInt();
ignoreUnregisteredUsers = settings->value("chat/ignore_unregistered", false).toBool(); ignoreUnregisteredUsers = settings->value("chat/ignore_unregistered", false).toBool();
} }
@ -247,6 +248,12 @@ void SettingsCache::setPriceTagFeature(int _priceTagFeature)
emit priceTagFeatureChanged(priceTagFeature); emit priceTagFeatureChanged(priceTagFeature);
} }
void SettingsCache::setPriceTagSource(int _priceTagSource)
{
priceTagSource = _priceTagSource;
settings->setValue("deckeditor/pricetagsource", priceTagSource);
}
void SettingsCache::setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers) void SettingsCache::setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers)
{ {
ignoreUnregisteredUsers = _ignoreUnregisteredUsers; ignoreUnregisteredUsers = _ignoreUnregisteredUsers;

View file

@ -53,6 +53,7 @@ private:
bool soundEnabled; bool soundEnabled;
QString soundPath; QString soundPath;
bool priceTagFeature; bool priceTagFeature;
int priceTagSource;
bool ignoreUnregisteredUsers; bool ignoreUnregisteredUsers;
QString picUrl; QString picUrl;
QString picUrlHq; QString picUrlHq;
@ -87,6 +88,7 @@ public:
bool getSoundEnabled() const { return soundEnabled; } bool getSoundEnabled() const { return soundEnabled; }
QString getSoundPath() const { return soundPath; } QString getSoundPath() const { return soundPath; }
bool getPriceTagFeature() const { return priceTagFeature; } bool getPriceTagFeature() const { return priceTagFeature; }
int getPriceTagSource() const { return priceTagSource; }
bool getIgnoreUnregisteredUsers() const { return ignoreUnregisteredUsers; } bool getIgnoreUnregisteredUsers() const { return ignoreUnregisteredUsers; }
QString getPicUrl() const { return picUrl; } QString getPicUrl() const { return picUrl; }
QString getPicUrlHq() const { return picUrlHq; } QString getPicUrlHq() const { return picUrlHq; }
@ -121,6 +123,7 @@ public slots:
void setSoundEnabled(int _soundEnabled); void setSoundEnabled(int _soundEnabled);
void setSoundPath(const QString &_soundPath); void setSoundPath(const QString &_soundPath);
void setPriceTagFeature(int _priceTagFeature); void setPriceTagFeature(int _priceTagFeature);
void setPriceTagSource(int _priceTagSource);
void setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers); void setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers);
void setPicUrl(const QString &_picUrl); void setPicUrl(const QString &_picUrl);
void setPicUrlHq(const QString &_picUrlHq); void setPicUrlHq(const QString &_picUrlHq);

View file

@ -31,6 +31,7 @@ public:
virtual QString getTabText() const = 0; virtual QString getTabText() const = 0;
virtual void retranslateUi() = 0; virtual void retranslateUi() = 0;
virtual void closeRequest() { } virtual void closeRequest() { }
virtual void tabActivated() { }
}; };
#endif #endif

View file

@ -512,7 +512,10 @@ void TabDeckEditor::actPrintDeck()
void TabDeckEditor::actAnalyzeDeck() void TabDeckEditor::actAnalyzeDeck()
{ {
DeckStatsInterface *interface = new DeckStatsInterface(this); // it deletes itself when done DeckStatsInterface *interface = new DeckStatsInterface(
*databaseModel->getDatabase(),
this
); // it deletes itself when done
interface->analyzeDeck(deckModel->getDeckList()); interface->analyzeDeck(deckModel->getDeckList());
} }
@ -643,12 +646,25 @@ void TabDeckEditor::actDecrement()
void TabDeckEditor::setPriceTagFeatureEnabled(int enabled) void TabDeckEditor::setPriceTagFeatureEnabled(int enabled)
{ {
aUpdatePrices->setVisible(enabled); aUpdatePrices->setVisible(enabled);
deckModel->pricesUpdated();
} }
void TabDeckEditor::actUpdatePrices() void TabDeckEditor::actUpdatePrices()
{ {
aUpdatePrices->setDisabled(true); aUpdatePrices->setDisabled(true);
PriceUpdater *up = new PriceUpdater(deckModel->getDeckList()); AbstractPriceUpdater *up;
switch(settingsCache->getPriceTagSource())
{
case AbstractPriceUpdater::DBPriceSource:
up = new DBPriceUpdater(deckModel->getDeckList());
break;
case AbstractPriceUpdater::BLPPriceSource:
default:
up = new BLPPriceUpdater(deckModel->getDeckList());
break;
}
connect(up, SIGNAL(finishedUpdate()), this, SLOT(finishedUpdatingPrices())); connect(up, SIGNAL(finishedUpdate()), this, SLOT(finishedUpdatingPrices()));
up->updatePrices(); up->updatePrices();
} }

View file

@ -255,7 +255,7 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay)
// Create list: event number -> time [ms] // Create list: event number -> time [ms]
// Distribute simultaneous events evenly across 1 second. // Distribute simultaneous events evenly across 1 second.
int lastEventTimestamp = -1; unsigned int lastEventTimestamp = 0;
const int eventCount = replay->event_list_size(); const int eventCount = replay->event_list_size();
for (int i = 0; i < eventCount; ++i) { for (int i = 0; i < eventCount; ++i) {
int j = i + 1; int j = i + 1;

View file

@ -49,6 +49,12 @@ void TabMessage::retranslateUi()
aLeave->setText(tr("&Leave")); aLeave->setText(tr("&Leave"));
} }
void TabMessage::tabActivated()
{
if(!sayEdit->hasFocus())
sayEdit->setFocus();
}
QString TabMessage::getUserName() const QString TabMessage::getUserName() const
{ {
return QString::fromStdString(otherUserInfo->name()); return QString::fromStdString(otherUserInfo->name());

View file

@ -34,6 +34,7 @@ public:
~TabMessage(); ~TabMessage();
void retranslateUi(); void retranslateUi();
void closeRequest(); void closeRequest();
void tabActivated();
QString getUserName() const; QString getUserName() const;
QString getTabText() const; QString getTabText() const;

View file

@ -202,7 +202,7 @@ void TabReplays::actDownload()
client->sendCommand(pend); client->sendCommand(pend);
} }
void TabReplays::downloadFinished(const Response &r, const CommandContainer &commandContainer, const QVariant &extraData) void TabReplays::downloadFinished(const Response &r, const CommandContainer & /* commandContainer */, const QVariant &extraData)
{ {
if (r.response_code() != Response::RespOk) if (r.response_code() != Response::RespOk)
return; return;

View file

@ -121,6 +121,12 @@ void TabRoom::closeRequest()
actLeaveRoom(); actLeaveRoom();
} }
void TabRoom::tabActivated()
{
if(!sayEdit->hasFocus())
sayEdit->setFocus();
}
QString TabRoom::sanitizeHtml(QString dirty) const QString TabRoom::sanitizeHtml(QString dirty) const
{ {
return dirty return dirty

View file

@ -64,6 +64,7 @@ public:
~TabRoom(); ~TabRoom();
void retranslateUi(); void retranslateUi();
void closeRequest(); void closeRequest();
void tabActivated();
void processRoomEvent(const RoomEvent &event); void processRoomEvent(const RoomEvent &event);
int getRoomId() const { return roomId; } int getRoomId() const { return roomId; }
const QMap<int, QString> &getGameTypes() const { return gameTypes; } const QMap<int, QString> &getGameTypes() const { return gameTypes; }

View file

@ -76,7 +76,7 @@ void CloseButton::paintEvent(QPaintEvent * /*event*/)
} }
TabSupervisor::TabSupervisor(AbstractClient *_client, QWidget *parent) TabSupervisor::TabSupervisor(AbstractClient *_client, QWidget *parent)
: QTabWidget(parent), userInfo(0), client(_client), tabUserLists(0), tabServer(0), tabDeckStorage(0), tabAdmin(0), tabReplays(0) : QTabWidget(parent), userInfo(0), client(_client), tabServer(0), tabUserLists(0), tabDeckStorage(0), tabReplays(0), tabAdmin(0)
{ {
tabChangedIcon = new QIcon(":/resources/icon_tab_changed.svg"); tabChangedIcon = new QIcon(":/resources/icon_tab_changed.svg");
setElideMode(Qt::ElideRight); setElideMode(Qt::ElideRight);
@ -479,6 +479,7 @@ void TabSupervisor::updateCurrent(int index)
tab->setContentsChanged(false); tab->setContentsChanged(false);
} }
emit setMenu(static_cast<Tab *>(widget(index))->getTabMenus()); emit setMenu(static_cast<Tab *>(widget(index))->getTabMenus());
tab->tabActivated();
} else } else
emit setMenu(); emit setMenu();
} }

View file

@ -102,7 +102,7 @@ void UserContextMenu::banUser_dialogFinished()
client->sendCommand(client->prepareModeratorCommand(cmd)); client->sendCommand(client->prepareModeratorCommand(cmd));
} }
void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName, UserLevelFlags userLevel, int playerId) void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName, UserLevelFlags userLevel, bool online, int playerId)
{ {
aUserName->setText(userName); aUserName->setText(userName);
@ -132,7 +132,8 @@ void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName
menu->addAction(aBan); menu->addAction(aBan);
} }
bool anotherUser = userName != QString::fromStdString(tabSupervisor->getUserInfo()->name()); bool anotherUser = userName != QString::fromStdString(tabSupervisor->getUserInfo()->name());
aChat->setEnabled(anotherUser); aDetails->setEnabled(online);
aChat->setEnabled(anotherUser && online);
aShowGames->setEnabled(anotherUser); aShowGames->setEnabled(anotherUser);
aAddToBuddyList->setEnabled(anotherUser); aAddToBuddyList->setEnabled(anotherUser);
aRemoveFromBuddyList->setEnabled(anotherUser); aRemoveFromBuddyList->setEnabled(anotherUser);

View file

@ -36,7 +36,7 @@ private slots:
public: public:
UserContextMenu(const TabSupervisor *_tabSupervisor, QWidget *_parent, TabGame *_game = 0); UserContextMenu(const TabSupervisor *_tabSupervisor, QWidget *_parent, TabGame *_game = 0);
void retranslateUi(); void retranslateUi();
void showContextMenu(const QPoint &pos, const QString &userName, UserLevelFlags userLevel, int playerId = -1); void showContextMenu(const QPoint &pos, const QString &userName, UserLevelFlags userLevel, bool online = true, int playerId = -1);
}; };
#endif #endif

View file

@ -311,8 +311,9 @@ void UserList::userClicked(QTreeWidgetItem *item, int /*column*/)
void UserList::showContextMenu(const QPoint &pos, const QModelIndex &index) void UserList::showContextMenu(const QPoint &pos, const QModelIndex &index)
{ {
const ServerInfo_User &userInfo = static_cast<UserListTWI *>(userTree->topLevelItem(index.row()))->getUserInfo(); const ServerInfo_User &userInfo = static_cast<UserListTWI *>(userTree->topLevelItem(index.row()))->getUserInfo();
bool online = index.sibling(index.row(), 0).data(Qt::UserRole + 1).toBool();
userContextMenu->showContextMenu(pos, QString::fromStdString(userInfo.name()), UserLevelFlags(userInfo.user_level())); userContextMenu->showContextMenu(pos, QString::fromStdString(userInfo.name()), UserLevelFlags(userInfo.user_level()), online);
} }
void UserList::sortItems() void UserList::sortItems()

View file

@ -241,7 +241,7 @@ void MainWindow::loginError(Response::ResponseCode r, QString reasonStr, quint32
{ {
switch (r) { switch (r) {
case Response::RespWrongPassword: case Response::RespWrongPassword:
QMessageBox::critical(this, tr("Error"), tr("Invalid login data.")); QMessageBox::critical(this, tr("Error"), tr("Incorrect username or password. Please check your authentication information and try again."));
break; break;
case Response::RespWouldOverwriteOldSession: case Response::RespWouldOverwriteOldSession:
QMessageBox::critical(this, tr("Error"), tr("There is already an active session using this user name.\nPlease close that session first and re-login.")); QMessageBox::critical(this, tr("Error"), tr("There is already an active session using this user name.\nPlease close that session first and re-login."));

View file

@ -355,7 +355,8 @@ DeckList::DeckList()
// TODO: http://qt-project.org/doc/qt-4.8/qobject.html#no-copy-constructor-or-assignment-operator // TODO: http://qt-project.org/doc/qt-4.8/qobject.html#no-copy-constructor-or-assignment-operator
DeckList::DeckList(const DeckList &other) DeckList::DeckList(const DeckList &other)
: name(other.name), : QObject(),
name(other.name),
comments(other.comments), comments(other.comments),
deckHash(other.deckHash) deckHash(other.deckHash)
{ {
@ -547,14 +548,21 @@ bool DeckList::loadFromStream_Plain(QTextStream &in)
line.remove(rx); line.remove(rx);
line = line.simplified(); line = line.simplified();
int i = line.indexOf(' '); int i = line.indexOf(' ');
int cardNameStart = i + 1;
// If the count ends with an 'x', ignore it. For example,
// "4x Storm Crow" will count 4 correctly.
if (i > 0 && line[i - 1] == 'x') {
i--;
}
bool ok; bool ok;
int number = line.left(i).toInt(&ok); int number = line.left(i).toInt(&ok);
if (!ok) if (!ok)
continue; continue;
QString cardName = line.mid(i + 1); QString cardName = line.mid(cardNameStart);
// Common differences between cockatrice's card names // Common differences between cockatrice's card names
// and what's commonly used in decklists // and what's commonly used in decklists
rx.setPattern(""); rx.setPattern("");
@ -563,9 +571,20 @@ bool DeckList::loadFromStream_Plain(QTextStream &in)
cardName.replace(rx, "AE"); cardName.replace(rx, "AE");
rx.setPattern("^Aether"); rx.setPattern("^Aether");
cardName.replace(rx, "AEther"); cardName.replace(rx, "AEther");
rx.setPattern("\\s*[&|/]{1,2}\\s*"); rx.setPattern("\\s*[|/]{1,2}\\s*");
cardName.replace(rx, " // "); cardName.replace(rx, " // ");
// Replace only if the ampersand is preceded by a non-capital letter,
// as would happen with acronyms. So 'Fire & Ice' is replaced but not
// 'R&D' or 'R & D'.
//
// Qt regexes don't support lookbehind so we capture and replace
// instead.
rx.setPattern("([^A-Z])\\s*&\\s*");
if (rx.indexIn(cardName) != -1) {
cardName.replace(rx, QString("%1 // ").arg(rx.cap(1)));
}
++okRows; ++okRows;
new DecklistCardNode(cardName, number, zone); new DecklistCardNode(cardName, number, zone);
} }
@ -579,16 +598,30 @@ bool DeckList::loadFromFile_Plain(QIODevice *device)
return loadFromStream_Plain(in); return loadFromStream_Plain(in);
} }
struct WriteToStream {
QTextStream &stream;
WriteToStream(QTextStream &_stream) : stream(_stream) {}
void operator()(
const InnerDecklistNode *node,
const DecklistCardNode *card
) {
if (node->getName() == "side") {
stream << "SB: ";
}
stream << QString("%1 %2\n").arg(
card->getNumber()
).arg(
card->getName()
);
}
};
bool DeckList::saveToStream_Plain(QTextStream &out) bool DeckList::saveToStream_Plain(QTextStream &out)
{ {
// Support for this is only possible if the internal structure doesn't get more complicated. WriteToStream writeToStream(out);
for (int i = 0; i < root->size(); i++) { forEachCard(writeToStream);
InnerDecklistNode *node = dynamic_cast<InnerDecklistNode *>(root->at(i));
for (int j = 0; j < node->size(); j++) {
DecklistCardNode *card = dynamic_cast<DecklistCardNode *>(node->at(j));
out << QString("%1%2 %3\n").arg(node->getName() == "side" ? "SB: " : "").arg(card->getNumber()).arg(card->getName());
}
}
return true; return true;
} }

View file

@ -117,6 +117,7 @@ public:
QString getName() const { return name; } QString getName() const { return name; }
void setName(const QString &_name) { name = _name; } void setName(const QString &_name) { name = _name; }
float getPrice() const { return price; } float getPrice() const { return price; }
void setPrice(const float _price) { price = _price; } void setPrice(const float _price) { price = _price; }
}; };
@ -169,6 +170,28 @@ public:
InnerDecklistNode *getRoot() const { return root; } InnerDecklistNode *getRoot() const { return root; }
DecklistCardNode *addCard(const QString &cardName, const QString &zoneName); DecklistCardNode *addCard(const QString &cardName, const QString &zoneName);
bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = 0); bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = 0);
/**
* Calls a given function object for each card in the deck. It must
* take a InnerDecklistNode* as its first argument and a
* DecklistCardNode* as its second.
*/
template <typename Callback>
void forEachCard(Callback &callback) const {
// Support for this is only possible if the internal structure
// doesn't get more complicated.
for (int i = 0; i < root->size(); i++) {
const InnerDecklistNode *node =
dynamic_cast<InnerDecklistNode *>(root->at(i));
for (int j = 0; j < node->size(); j++) {
const DecklistCardNode *card =
dynamic_cast<DecklistCardNode *>(
node->at(j)
);
callback(node, card);
}
}
}
}; };
#endif #endif

View file

@ -97,7 +97,7 @@ protected slots:
void externalGameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId); void externalGameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId);
void externalResponseReceived(const Response &resp, qint64 sessionId); void externalResponseReceived(const Response &resp, qint64 sessionId);
virtual void doSendIslMessage(const IslMessage &msg, int serverId) { } virtual void doSendIslMessage(const IslMessage & /* msg */, int /* serverId */) { }
protected: protected:
void prepareDestroy(); void prepareDestroy();
void setDatabaseInterface(Server_DatabaseInterface *_databaseInterface); void setDatabaseInterface(Server_DatabaseInterface *_databaseInterface);

View file

@ -12,18 +12,18 @@ public:
: QObject(parent) { } : QObject(parent) { }
virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft) = 0; virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft) = 0;
virtual bool userExists(const QString &user) { return false; } virtual bool userExists(const QString & /* user */) { return false; }
virtual QMap<QString, ServerInfo_User> getBuddyList(const QString &name) { return QMap<QString, ServerInfo_User>(); } virtual QMap<QString, ServerInfo_User> getBuddyList(const QString & /* name */) { return QMap<QString, ServerInfo_User>(); }
virtual QMap<QString, ServerInfo_User> getIgnoreList(const QString &name) { return QMap<QString, ServerInfo_User>(); } virtual QMap<QString, ServerInfo_User> getIgnoreList(const QString & /* name */) { return QMap<QString, ServerInfo_User>(); }
virtual bool isInBuddyList(const QString &whoseList, const QString &who) { return false; } virtual bool isInBuddyList(const QString & /* whoseList */, const QString & /* who */) { return false; }
virtual bool isInIgnoreList(const QString &whoseList, const QString &who) { return false; } virtual bool isInIgnoreList(const QString & /* whoseList */, const QString & /* who */) { return false; }
virtual ServerInfo_User getUserData(const QString &name, bool withId = false) = 0; virtual ServerInfo_User getUserData(const QString &name, bool withId = false) = 0;
virtual void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, const QSet<QString> &allPlayersEver, const QSet<QString> &allSpectatorsEver, const QList<GameReplay *> &replayList) { } virtual void storeGameInformation(const QString & /* roomName */, const QStringList & /* roomGameTypes */, const ServerInfo_Game & /* gameInfo */, const QSet<QString> & /* allPlayersEver */, const QSet<QString> & /* allSpectatorsEver */, const QList<GameReplay *> & /* replayList */) { }
virtual DeckList *getDeckFromDatabase(int deckId, int userId) { return 0; } virtual DeckList *getDeckFromDatabase(int /* deckId */, int /* userId */) { return 0; }
virtual qint64 startSession(const QString &userName, const QString &address) { return 0; } virtual qint64 startSession(const QString & /* userName */, const QString & /* address */) { return 0; }
public slots: public slots:
virtual void endSession(qint64 sessionId) { } virtual void endSession(qint64 /* sessionId */ ) { }
public: public:
virtual int getNextGameId() = 0; virtual int getNextGameId() = 0;
virtual int getNextReplayId() = 0; virtual int getNextReplayId() = 0;
@ -31,7 +31,7 @@ public:
virtual void clearSessionTables() { } virtual void clearSessionTables() { }
virtual void lockSessionTables() { } virtual void lockSessionTables() { }
virtual void unlockSessionTables() { } virtual void unlockSessionTables() { }
virtual bool userSessionExists(const QString &userName) { return false; } virtual bool userSessionExists(const QString & /* userName */) { return false; }
}; };
#endif #endif

View file

@ -1594,7 +1594,7 @@ Response::ResponseCode Server_Player::cmdRevealCards(const Command_RevealCards &
return Response::RespOk; return Response::RespOk;
} }
Response::ResponseCode Server_Player::cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, ResponseContainer &rc, GameEventStorage &ges) Response::ResponseCode Server_Player::cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, ResponseContainer & /* rc */, GameEventStorage &ges)
{ {
Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone_name())); Server_CardZone *zone = zones.value(QString::fromStdString(cmd.zone_name()));
if (!zone) if (!zone)

View file

@ -49,7 +49,7 @@ protected:
AuthenticationResult authState; AuthenticationResult authState;
bool acceptsUserListChanges; bool acceptsUserListChanges;
bool acceptsRoomListChanges; bool acceptsRoomListChanges;
virtual void logDebugMessage(const QString &message) { } virtual void logDebugMessage(const QString & /* message */) { }
private: private:
QList<int> messageSizeOverTime, messageCountOverTime; QList<int> messageSizeOverTime, messageCountOverTime;
int timeRunning, lastDataReceived; int timeRunning, lastDataReceived;
@ -71,13 +71,13 @@ private:
Response::ResponseCode cmdJoinGame(const Command_JoinGame &cmd, Server_Room *room, ResponseContainer &rc); Response::ResponseCode cmdJoinGame(const Command_JoinGame &cmd, Server_Room *room, ResponseContainer &rc);
Response::ResponseCode processSessionCommandContainer(const CommandContainer &cont, ResponseContainer &rc); Response::ResponseCode processSessionCommandContainer(const CommandContainer &cont, ResponseContainer &rc);
virtual Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc) { return Response::RespFunctionNotAllowed; } virtual Response::ResponseCode processExtendedSessionCommand(int /* cmdType */, const SessionCommand & /* cmd */, ResponseContainer & /* rc */) { return Response::RespFunctionNotAllowed; }
Response::ResponseCode processRoomCommandContainer(const CommandContainer &cont, ResponseContainer &rc); Response::ResponseCode processRoomCommandContainer(const CommandContainer &cont, ResponseContainer &rc);
Response::ResponseCode processGameCommandContainer(const CommandContainer &cont, ResponseContainer &rc); Response::ResponseCode processGameCommandContainer(const CommandContainer &cont, ResponseContainer &rc);
Response::ResponseCode processModeratorCommandContainer(const CommandContainer &cont, ResponseContainer &rc); Response::ResponseCode processModeratorCommandContainer(const CommandContainer &cont, ResponseContainer &rc);
virtual Response::ResponseCode processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc) { return Response::RespFunctionNotAllowed; } virtual Response::ResponseCode processExtendedModeratorCommand(int /* cmdType */, const ModeratorCommand & /* cmd */, ResponseContainer & /* rc */) { return Response::RespFunctionNotAllowed; }
Response::ResponseCode processAdminCommandContainer(const CommandContainer &cont, ResponseContainer &rc); Response::ResponseCode processAdminCommandContainer(const CommandContainer &cont, ResponseContainer &rc);
virtual Response::ResponseCode processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc) { return Response::RespFunctionNotAllowed; } virtual Response::ResponseCode processExtendedAdminCommand(int /* cmdType */, const AdminCommand & /* cmd */, ResponseContainer & /* rc */) { return Response::RespFunctionNotAllowed; }
private slots: private slots:
void pingClockTimeout(); void pingClockTimeout();
public slots: public slots:

56
doc/cards.xsd Normal file
View file

@ -0,0 +1,56 @@
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="cockatrice_carddatabase">
<xs:complexType>
<xs:sequence>
<xs:element name="sets">
<xs:complexType>
<xs:sequence>
<xs:element name="set" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="name"/>
<xs:element type="xs:string" name="longname"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="cards">
<xs:complexType>
<xs:sequence>
<xs:element name="card" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="name"/>
<xs:element name="set" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:int" name="muId" use="optional"/>
<xs:attribute type="xs:anyURI" name="picUrl" use="optional"/>
<xs:attribute type="xs:anyURI" name="picUrlHq" use="optional"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element type="xs:string" name="color" minOccurs="0" maxOccurs="unbounded"/>
<xs:element type="xs:string" name="manacost"/>
<xs:element type="xs:string" name="type"/>
<xs:element type="xs:string" name="pt" minOccurs="0"/>
<xs:element type="xs:integer" name="tablerow"/>
<xs:element type="xs:string" name="text"/>
<xs:element type="xs:boolean" name="cipt" minOccurs="0"/>
<xs:element type="xs:integer" name="loyalty" minOccurs="0"/>
<xs:element type="xs:boolean" name="token" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute type="xs:integer" name="version"/>
</xs:complexType>
</xs:element>
</xs:schema>

View file

@ -16,6 +16,19 @@ SET(oracle_SOURCES
../cockatrice/src/qt-json/json.cpp ../cockatrice/src/qt-json/json.cpp
) )
set(oracle_RESOURCES oracle.qrc)
if(WIN32)
set(oracle_SOURCES ${oracle_SOURCES} oracle.rc)
endif(WIN32)
if(APPLE)
set(MACOSX_BUNDLE_ICON_FILE appicon.icns)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
set(oracle_SOURCES ${oracle_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns)
ENDIF(APPLE)
set(ORACLE_LIBS) set(ORACLE_LIBS)
# Qt4 stuff # Qt4 stuff
@ -29,6 +42,10 @@ if(Qt4_FOUND)
include_directories(${QT_INCLUDES}) include_directories(${QT_INCLUDES})
LIST(APPEND ORACLE_LIBS ${QT_QTMAIN_LIBRARY}) LIST(APPEND ORACLE_LIBS ${QT_QTMAIN_LIBRARY})
LIST(APPEND ORACLE_LIBS ${QT_LIBRARIES}) LIST(APPEND ORACLE_LIBS ${QT_LIBRARIES})
# Let cmake chew Qt4's translations and resource files
# Note: header files are MOC-ed automatically by cmake
QT4_ADD_RESOURCES(oracle_RESOURCES_RCC ${oracle_RESOURCES})
endif() endif()
# qt5 stuff # qt5 stuff
@ -64,6 +81,10 @@ if(Qt5Widgets_FOUND)
list(APPEND ORACLE_LIBS Svg) list(APPEND ORACLE_LIBS Svg)
endif() endif()
# Let cmake chew Qt5's translations and resource files
# Note: header files are MOC-ed automatically by cmake
QT5_ADD_RESOURCES(oracle_RESOURCES_RCC ${oracle_RESOURCES})
# guess plugins and libraries directory # guess plugins and libraries directory
set(QT_PLUGINS_DIR "${Qt5Widgets_DIR}/../../../plugins") set(QT_PLUGINS_DIR "${Qt5Widgets_DIR}/../../../plugins")
get_target_property(QT_LIBRARY_DIR Qt5::Core LOCATION) get_target_property(QT_LIBRARY_DIR Qt5::Core LOCATION)
@ -73,7 +94,7 @@ endif()
INCLUDE_DIRECTORIES(../cockatrice/src) INCLUDE_DIRECTORIES(../cockatrice/src)
# Build oracle binary and link it # Build oracle binary and link it
ADD_EXECUTABLE(oracle WIN32 MACOSX_BUNDLE ${oracle_SOURCES} ${oracle_MOC_SRCS}) ADD_EXECUTABLE(oracle WIN32 MACOSX_BUNDLE ${oracle_SOURCES} ${oracle_RESOURCES_RCC} ${oracle_MOC_SRCS})
if(Qt4_FOUND) if(Qt4_FOUND)
if(MSVC) if(MSVC)
@ -94,6 +115,8 @@ if(UNIX)
else() else()
# Assume linux # Assume linux
INSTALL(TARGETS oracle RUNTIME DESTINATION bin/) INSTALL(TARGETS oracle RUNTIME DESTINATION bin/)
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/oracle.png DESTINATION ${ICONDIR}/hicolor/48x48/apps)
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/oracle.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps)
endif() endif()
elseif(WIN32) elseif(WIN32)
INSTALL(TARGETS oracle RUNTIME DESTINATION ./) INSTALL(TARGETS oracle RUNTIME DESTINATION ./)

View file

@ -4,5 +4,5 @@ Version=1.0
Type=Application Type=Application
Name=Cockatrice Oracle downloader Name=Cockatrice Oracle downloader
Exec=oracle Exec=oracle
Icon=cockatrice Icon=oracle
Categories=Game;CardGame; Categories=Game;CardGame;

5
oracle/oracle.qrc Normal file
View file

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/" >
<file alias="resources/appicon.svg">resources/oracle.svg</file>
</qresource>
</RCC>

1
oracle/oracle.rc Normal file
View file

@ -0,0 +1 @@
ID1_ICON1 ICON DISCARDABLE "resources/appicon.ico"

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

BIN
oracle/resources/oracle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

228
oracle/resources/oracle.svg Normal file
View file

@ -0,0 +1,228 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="300.0036"
height="300"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.48.2 r9819"
sodipodi:docname="back.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.0"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs4">
<linearGradient
id="linearGradient3169">
<stop
style="stop-color:#0000ff;stop-opacity:1;"
offset="0"
id="stop3171" />
<stop
style="stop-color:#000067;stop-opacity:1;"
offset="1"
id="stop3173" />
</linearGradient>
<linearGradient
id="linearGradient4766">
<stop
style="stop-color:#784421;stop-opacity:1;"
offset="0"
id="stop4768" />
<stop
style="stop-color:#3d2210;stop-opacity:0;"
offset="1"
id="stop4770" />
</linearGradient>
<linearGradient
id="linearGradient4758">
<stop
style="stop-color:#a05a2c;stop-opacity:1;"
offset="0"
id="stop4760" />
<stop
style="stop-color:#3d2210;stop-opacity:1;"
offset="1"
id="stop4762" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<inkscape:perspective
id="perspective2484"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 526.18109 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4758"
id="linearGradient4764"
x1="466.09601"
y1="485.96021"
x2="715.14801"
y2="485.96021"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4766"
id="linearGradient4772"
x1="496.548"
y1="485.26816"
x2="683.31201"
y2="485.26816"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4766"
id="linearGradient2396"
gradientUnits="userSpaceOnUse"
x1="661.24402"
y1="602.90814"
x2="431.5"
y2="201.5482"
gradientTransform="matrix(0.9650128,0,0,0.9948433,-449.70565,-312.80927)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4758"
id="linearGradient2399"
gradientUnits="userSpaceOnUse"
x1="590.62201"
y1="434.7522"
x2="698.54004"
y2="517.79218"
gradientTransform="matrix(0.9650128,0,0,0.9948433,-449.70565,-312.80927)" />
<filter
inkscape:collect="always"
id="filter3287"
color-interpolation-filters="sRGB">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.88403668"
id="feGaussianBlur3289" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4766"
id="linearGradient3057"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.84875138,0,0,0.874988,-395.5345,-274.89225)"
x1="661.24402"
y1="602.90814"
x2="431.5"
y2="201.5482" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4758"
id="linearGradient3059"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.84875138,0,0,0.874988,-395.5345,-274.89225)"
x1="590.62201"
y1="434.7522"
x2="698.54004"
y2="517.79218" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4911765"
inkscape:cx="164.24505"
inkscape:cy="113.77996"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1280"
inkscape:window-height="725"
inkscape:window-x="0"
inkscape:window-y="25"
fit-margin-left="44.18"
fit-margin-right="44.18"
fit-margin-top="0"
fit-margin-bottom="0"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(44.245053,-0.31708717)">
<rect
style="fill:url(#linearGradient3059);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:17.23539734;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect2490"
width="194.4082"
height="282.76462"
x="8.5526457"
y="8.9347858"
inkscape:export-xdpi="969.46002"
inkscape:export-ydpi="969.46002" />
<rect
style="fill:url(#linearGradient3057);fill-opacity:1;stroke:#000000;stroke-width:1.72353971;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect3302"
width="156.81871"
height="244.01315"
x="26.760056"
y="27.704998" />
<path
sodipodi:type="arc"
style="opacity:0.39208954;fill:#a05a2c;stroke:#000000;stroke-width:2.00029755;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3287)"
id="path3304"
sodipodi:cx="589.58398"
sodipodi:cy="489.76617"
sodipodi:rx="71.968002"
sodipodi:ry="128.71201"
d="m 661.55199,489.76617 c 0,71.08568 -32.22118,128.71201 -71.96801,128.71201 -39.74683,0 -71.968,-57.62633 -71.968,-128.71201 0,-71.08567 32.22117,-128.712 71.968,-128.712 39.74683,0 71.96801,57.62633 71.96801,128.712 z"
transform="matrix(1.0026379,0,0,0.89548037,-485.67617,-289.46994)" />
<flowRoot
xml:space="preserve"
id="flowRoot3021"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><flowRegion
id="flowRegion3023"><rect
id="rect3025"
width="151.92514"
height="235.58012"
x="50.000679"
y="60.188515" /></flowRegion><flowPara
id="flowPara3027"></flowPara></flowRoot> <text
xml:space="preserve"
style="font-size:35.18093872px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Songti SC;-inkscape-font-specification:Songti SC"
x="-14.38486"
y="244.29784"
id="text3029"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3031"
x="-14.38486"
y="244.29784"
style="font-size:390.50842285px;font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;fill:#ffffff;font-family:STKaiti;-inkscape-font-specification:STKaiti Bold Italic">o</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.9 KiB

View file

@ -1,5 +1,6 @@
#include <QApplication> #include <QApplication>
#include <QTextCodec> #include <QTextCodec>
#include <QIcon>
#include "oraclewizard.h" #include "oraclewizard.h"
#include "settingscache.h" #include "settingscache.h"
@ -22,6 +23,10 @@ int main(int argc, char *argv[])
settingsCache = new SettingsCache; settingsCache = new SettingsCache;
OracleWizard wizard; OracleWizard wizard;
QIcon icon(":/resources/appicon.svg");
wizard.setWindowIcon(icon);
wizard.show(); wizard.show();
return app.exec(); return app.exec();

View file

@ -80,8 +80,8 @@ CardInfo *OracleImporter::addCard(const QString &setName,
cardCost.remove(QChar('}')); cardCost.remove(QChar('}'));
CardInfo *card; CardInfo *card;
if (cardHash.contains(cardName)) { if (cards.contains(cardName)) {
card = cardHash.value(cardName); card = cards.value(cardName);
if (splitCard && !card->getText().contains(fullCardText)) if (splitCard && !card->getText().contains(fullCardText))
card->setText(card->getText() + "\n---\n" + fullCardText); card->setText(card->getText() + "\n---\n" + fullCardText);
} else { } else {
@ -121,7 +121,7 @@ CardInfo *OracleImporter::addCard(const QString &setName,
tableRow = 2; tableRow = 2;
card->setTableRow(tableRow); card->setTableRow(tableRow);
cardHash.insert(cardName, card); cards.insert(cardName, card);
} }
card->setMuId(setName, cardId); card->setMuId(setName, cardId);
@ -141,6 +141,7 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data)
QString cardText; QString cardText;
int cardId; int cardId;
int cardLoyalty; int cardLoyalty;
bool cardIsToken = false;
QMap<int, QVariantMap> splitCards; QMap<int, QVariantMap> splitCards;
while (it.hasNext()) { while (it.hasNext()) {
@ -201,9 +202,15 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data)
cardText = map.contains("text") ? map.value("text").toString() : QString(""); cardText = map.contains("text") ? map.value("text").toString() : QString("");
cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0; cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0;
cardLoyalty = map.contains("loyalty") ? map.value("loyalty").toInt() : 0; cardLoyalty = map.contains("loyalty") ? map.value("loyalty").toInt() : 0;
cardIsToken = map.value("layout") == "token";
// Distinguish Vanguard cards from regular cards of the same name.
if (map.value("layout") == "vanguard") {
cardName += " Avatar";
}
} }
CardInfo *card = addCard(set->getShortName(), cardName, false, cardId, cardCost, cardType, cardPT, cardLoyalty, cardText.split("\n")); CardInfo *card = addCard(set->getShortName(), cardName, cardIsToken, cardId, cardCost, cardType, cardPT, cardLoyalty, cardText.split("\n"));
if (!set->contains(card)) { if (!set->contains(card)) {
card->addToSet(set); card->addToSet(set);
@ -229,8 +236,8 @@ int OracleImporter::startImport()
continue; continue;
CardSet *set = new CardSet(curSet->getShortName(), curSet->getLongName()); CardSet *set = new CardSet(curSet->getShortName(), curSet->getLongName());
if (!setHash.contains(set->getShortName())) if (!sets.contains(set->getShortName()))
setHash.insert(set->getShortName(), set); sets.insert(set->getShortName(), set);
int setCards = importTextSpoiler(set, curSet->getCards()); int setCards = importTextSpoiler(set, curSet->getCards());

View file

@ -383,7 +383,7 @@ void SaveSetsPage::initializePage()
QMessageBox::critical(this, tr("Error"), tr("No set has been imported.")); QMessageBox::critical(this, tr("Error"), tr("No set has been imported."));
} }
void SaveSetsPage::updateTotalProgress(int cardsImported, int setIndex, const QString &setName) void SaveSetsPage::updateTotalProgress(int cardsImported, int /* setIndex */, const QString &setName)
{ {
if (setName.isEmpty()) { if (setName.isEmpty()) {
messageLog->append("<b>" + tr("Import finished: %1 cards.").arg(wizard()->importer->getCardList().size()) + "</b>"); messageLog->append("<b>" + tr("Import finished: %1 cards.").arg(wizard()->importer->getCardList().size()) + "</b>");
@ -418,9 +418,12 @@ bool SaveSetsPage::validatePage()
return false; return false;
} }
if (wizard()->importer->saveToFile(fileName)) if (wizard()->importer->saveToFile(fileName))
{
ok = true; ok = true;
else QMessageBox::information(this, tr("Success"), tr("The card database has been saved successfully."));
} else {
QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to the desired location.")); QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to the desired location."));
}
} while (!ok); } while (!ok);
return true; return true;

View file

@ -0,0 +1,3 @@
#!/bin/bash
# SCHEDULE WITH CRONTAB BASED ON TIME PERIOD REPLAYS SHOULD BE SAVED UNTIL (EX: SCHEDULE ONCE A WEEK TO KEEP A WEEKS WORTH OF REPLAYS IN THE DB)
mysql --defaults-file=./mysql.cnf -h localhost -e 'truncate table servatrice.cockatrice_replays;truncate table servatrice.cockatrice_replays_access'

View file

@ -0,0 +1,3 @@
#!/bin/bash
# SCHEDULE WITH CRONTAB TO RUN ONCE A MONTH
mysql --defaults-file=./mysql.cnf -h localhost -e "delete from servatrice.cockatrice_sessions where start_time < DATE_SUB(now(), INTERVAL 1 MONTH)"

View file

@ -0,0 +1,3 @@
[client]
user={db_username}
password={db_password}

View file

@ -29,6 +29,7 @@
#include "server_logger.h" #include "server_logger.h"
#include "rng_sfmt.h" #include "rng_sfmt.h"
#include "version_string.h" #include "version_string.h"
#include <google/protobuf/stubs/common.h>
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
#include <signal.h> #include <signal.h>
#endif #endif
@ -37,6 +38,23 @@ RNG_Abstract *rng;
ServerLogger *logger; ServerLogger *logger;
QThread *loggerThread; QThread *loggerThread;
/* Prototypes */
void testRNG();
void testHash();
#if QT_VERSION < 0x050000
void myMessageOutput(QtMsgType type, const char *msg);
void myMessageOutput2(QtMsgType type, const char *msg);
#else
void myMessageOutput(QtMsgType type, const QMessageLogContext &, const QString &msg);
void myMessageOutput2(QtMsgType type, const QMessageLogContext &, const QString &msg);
#endif
#ifdef Q_OS_UNIX
void sigSegvHandler(int sig);
#endif
/* Implementations */
void testRNG() void testRNG()
{ {
const int n = 500000; const int n = 500000;
@ -216,5 +234,8 @@ int main(int argc, char *argv[])
loggerThread->wait(); loggerThread->wait();
delete loggerThread; delete loggerThread;
// Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();
return retval; return retval;
} }

View file

@ -18,12 +18,15 @@ QString PasswordHasher::computeHash(const QString &password, const QString &salt
QByteArray passwordBuffer = (salt + password).toUtf8(); QByteArray passwordBuffer = (salt + password).toUtf8();
int hashLen = gcry_md_get_algo_dlen(algo); int hashLen = gcry_md_get_algo_dlen(algo);
char hash[hashLen], tmp[hashLen]; char *hash = new char[hashLen], *tmp = new char[hashLen];
gcry_md_hash_buffer(algo, hash, passwordBuffer.data(), passwordBuffer.size()); gcry_md_hash_buffer(algo, hash, passwordBuffer.data(), passwordBuffer.size());
for (int i = 1; i < rounds; ++i) { for (int i = 1; i < rounds; ++i) {
memcpy(tmp, hash, hashLen); memcpy(tmp, hash, hashLen);
gcry_md_hash_buffer(algo, hash, tmp, hashLen); gcry_md_hash_buffer(algo, hash, tmp, hashLen);
} }
return salt + QString(QByteArray(hash, hashLen).toBase64()); QString hashedPass = salt + QString(QByteArray(hash, hashLen).toBase64());
delete[] tmp;
delete[] hash;
return hashedPass;
} }

View file

@ -78,7 +78,11 @@ Servatrice_GameServer::~Servatrice_GameServer()
} }
} }
#if QT_VERSION < 0x050000
void Servatrice_GameServer::incomingConnection(int socketDescriptor) void Servatrice_GameServer::incomingConnection(int socketDescriptor)
#else
void Servatrice_GameServer::incomingConnection(qintptr socketDescriptor)
#endif
{ {
// Determine connection pool with smallest client count // Determine connection pool with smallest client count
int minClientCount = -1; int minClientCount = -1;

View file

@ -52,7 +52,11 @@ public:
Servatrice_GameServer(Servatrice *_server, int _numberPools, const QSqlDatabase &_sqlDatabase, QObject *parent = 0); Servatrice_GameServer(Servatrice *_server, int _numberPools, const QSqlDatabase &_sqlDatabase, QObject *parent = 0);
~Servatrice_GameServer(); ~Servatrice_GameServer();
protected: protected:
#if QT_VERSION < 0x050000
void incomingConnection(int socketDescriptor); void incomingConnection(int socketDescriptor);
#else
void incomingConnection(qintptr socketDescriptor);
#endif
}; };
class Servatrice_IslServer : public QTcpServer { class Servatrice_IslServer : public QTcpServer {

View file

@ -50,9 +50,9 @@ void ServerLogger::logMessage(QString message, void *caller)
callerString = QString::number((qulonglong) caller, 16) + " "; callerString = QString::number((qulonglong) caller, 16) + " ";
//filter out all log entries based on values in configuration file //filter out all log entries based on values in configuration file
QSettings *settings = new QSettings("servatrice.ini", QSettings::IniFormat); QSettings settings("servatrice.ini", QSettings::IniFormat);
bool shouldWeWriteLog = settings->value("server/writelog").toBool(); bool shouldWeWriteLog = settings.value("server/writelog").toBool();
QString logFilters = settings->value("server/logfilters").toString(); QString logFilters = settings.value("server/logfilters").toString();
QStringList listlogFilters = logFilters.split(",", QString::SkipEmptyParts); QStringList listlogFilters = logFilters.split(",", QString::SkipEmptyParts);
bool shouldWeSkipLine = false; bool shouldWeSkipLine = false;

View file

@ -2,7 +2,7 @@
if [[ $TRAVIS_OS_NAME == "osx" ]] ; then if [[ $TRAVIS_OS_NAME == "osx" ]] ; then
brew update brew update
brew install qt cmake protobuf libgcrypt brew install qt protobuf libgcrypt
else else
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -y qtmobility-dev libprotobuf-dev protobuf-compiler libqt4-dev sudo apt-get install -y qtmobility-dev libprotobuf-dev protobuf-compiler libqt4-dev