Merge pull request #111 from Daenyth/better-invalid-db-ux

Don't be a jerk when card database isn't usable.
This commit is contained in:
Gavin Bisesi 2014-06-22 15:28:45 -04:00
commit 1260ccc531
4 changed files with 109 additions and 41 deletions

View file

@ -14,7 +14,7 @@
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QDebug> #include <QDebug>
const int CardDatabase::versionNeeded = 2; const int CardDatabase::versionNeeded = 3;
CardSet::CardSet(const QString &_shortName, const QString &_longName) CardSet::CardSet(const QString &_shortName, const QString &_longName)
: shortName(_shortName), longName(_longName) : shortName(_shortName), longName(_longName)
@ -457,7 +457,7 @@ QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info)
} }
CardDatabase::CardDatabase(QObject *parent) CardDatabase::CardDatabase(QObject *parent)
: QObject(parent), loadSuccess(false), noCard(0) : QObject(parent), loadStatus(NotLoaded), noCard(0)
{ {
connect(settingsCache, SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged())); connect(settingsCache, SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged()));
connect(settingsCache, SIGNAL(cardDatabasePathChanged()), this, SLOT(loadCardDatabase())); connect(settingsCache, SIGNAL(cardDatabasePathChanged()), this, SLOT(loadCardDatabase()));
@ -636,13 +636,13 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
} }
} }
bool CardDatabase::loadFromFile(const QString &fileName, bool tokens) LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens)
{ {
QFile file(fileName); QFile file(fileName);
file.open(QIODevice::ReadOnly); file.open(QIODevice::ReadOnly);
if (!file.isOpen()) if (!file.isOpen())
return false; return FileError;
if (tokens) { if (tokens) {
QMutableHashIterator<QString, CardInfo *> i(cardHash); QMutableHashIterator<QString, CardInfo *> i(cardHash);
while (i.hasNext()) { while (i.hasNext()) {
@ -659,7 +659,7 @@ bool CardDatabase::loadFromFile(const QString &fileName, bool tokens)
delete setIt.value(); delete setIt.value();
} }
setHash.clear(); setHash.clear();
QMutableHashIterator<QString, CardInfo *> i(cardHash); QMutableHashIterator<QString, CardInfo *> i(cardHash);
while (i.hasNext()) { while (i.hasNext()) {
i.next(); i.next();
@ -675,9 +675,12 @@ bool CardDatabase::loadFromFile(const QString &fileName, bool tokens)
while (!xml.atEnd()) { while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::StartElement) { if (xml.readNext() == QXmlStreamReader::StartElement) {
if (xml.name() != "cockatrice_carddatabase") if (xml.name() != "cockatrice_carddatabase")
return false; return Invalid;
if (xml.attributes().value("version").toString().toInt() < versionNeeded) int version = xml.attributes().value("version").toString().toInt();
return false; if (version < versionNeeded) {
qDebug() << "loadFromFile(): Version too old: " << version;
return VersionTooOld;
}
while (!xml.atEnd()) { while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) if (xml.readNext() == QXmlStreamReader::EndElement)
break; break;
@ -689,7 +692,10 @@ bool CardDatabase::loadFromFile(const QString &fileName, bool tokens)
} }
} }
qDebug() << cardHash.size() << "cards in" << setHash.size() << "sets loaded"; qDebug() << cardHash.size() << "cards in" << setHash.size() << "sets loaded";
return !cardHash.isEmpty();
if (cardHash.isEmpty()) return NoCards;
return Ok;
} }
bool CardDatabase::saveToFile(const QString &fileName, bool tokens) bool CardDatabase::saveToFile(const QString &fileName, bool tokens)
@ -747,13 +753,13 @@ void CardDatabase::picDownloadHqChanged()
} }
} }
bool CardDatabase::loadCardDatabase(const QString &path, bool tokens) LoadStatus CardDatabase::loadCardDatabase(const QString &path, bool tokens)
{ {
bool tempLoadSuccess = false; LoadStatus tempLoadStatus = NotLoaded;
if (!path.isEmpty()) if (!path.isEmpty())
tempLoadSuccess = loadFromFile(path, tokens); tempLoadStatus = loadFromFile(path, tokens);
if (tempLoadSuccess) { if (tempLoadStatus == Ok) {
SetList allSets; SetList allSets;
QHashIterator<QString, CardSet *> setsIterator(setHash); QHashIterator<QString, CardSet *> setsIterator(setHash);
while (setsIterator.hasNext()) while (setsIterator.hasNext())
@ -761,14 +767,17 @@ bool CardDatabase::loadCardDatabase(const QString &path, bool tokens)
allSets.sortByKey(); allSets.sortByKey();
for (int i = 0; i < allSets.size(); ++i) for (int i = 0; i < allSets.size(); ++i)
allSets[i]->setSortKey(i); allSets[i]->setSortKey(i);
emit cardListChanged(); emit cardListChanged();
} }
if (!tokens) if (!tokens) {
loadSuccess = tempLoadSuccess; loadStatus = tempLoadStatus;
qDebug() << "loadCardDatabase(): Status = " << loadStatus;
return tempLoadSuccess; }
return tempLoadStatus;
} }
void CardDatabase::loadCardDatabase() void CardDatabase::loadCardDatabase()

View file

@ -158,16 +158,18 @@ signals:
void cardInfoChanged(CardInfo *card); void cardInfoChanged(CardInfo *card);
}; };
enum LoadStatus { Ok, VersionTooOld, Invalid, NotLoaded, FileError, NoCards };
class CardDatabase : public QObject { class CardDatabase : public QObject {
Q_OBJECT Q_OBJECT
protected: protected:
QHash<QString, CardInfo *> cardHash; QHash<QString, CardInfo *> cardHash;
QHash<QString, CardSet *> setHash; QHash<QString, CardSet *> setHash;
bool loadSuccess;
CardInfo *noCard; CardInfo *noCard;
QThread *pictureLoaderThread; QThread *pictureLoaderThread;
PictureLoader *pictureLoader; PictureLoader *pictureLoader;
LoadStatus loadStatus;
private: private:
static const int versionNeeded; static const int versionNeeded;
void loadCardsFromXml(QXmlStreamReader &xml); void loadCardsFromXml(QXmlStreamReader &xml);
@ -182,22 +184,23 @@ public:
CardSet *getSet(const QString &setName); CardSet *getSet(const QString &setName);
QList<CardInfo *> getCardList() const { return cardHash.values(); } QList<CardInfo *> getCardList() const { return cardHash.values(); }
SetList getSetList() const; SetList getSetList() const;
bool loadFromFile(const QString &fileName, bool tokens = false); LoadStatus loadFromFile(const QString &fileName, bool tokens = false);
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;
bool getLoadSuccess() const { return loadSuccess; } LoadStatus getLoadStatus() const { return loadStatus; }
bool getLoadSuccess() const { return loadStatus == Ok; }
void cacheCardPixmaps(const QStringList &cardNames); void cacheCardPixmaps(const QStringList &cardNames);
void loadImage(CardInfo *card); void loadImage(CardInfo *card);
public slots: public slots:
void clearPixmapCache(); void clearPixmapCache();
bool loadCardDatabase(const QString &path, bool tokens = false); LoadStatus loadCardDatabase(const QString &path, bool tokens = false);
private slots: private slots:
void imageLoaded(CardInfo *card, QImage image); void imageLoaded(CardInfo *card, QImage image);
void picDownloadChanged(); void picDownloadChanged();
void picDownloadHqChanged(); void picDownloadHqChanged();
void picsPathChanged(); void picsPathChanged();
void loadCardDatabase(); void loadCardDatabase();
void loadTokenDatabase(); void loadTokenDatabase();
signals: signals:

View file

@ -18,6 +18,7 @@
#include <QInputDialog> #include <QInputDialog>
#include <QSpinBox> #include <QSpinBox>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QDebug>
#include "carddatabase.h" #include "carddatabase.h"
#include "dlg_settings.h" #include "dlg_settings.h"
#include "main.h" #include "main.h"
@ -704,17 +705,65 @@ void DlgSettings::changeEvent(QEvent *event)
void DlgSettings::closeEvent(QCloseEvent *event) void DlgSettings::closeEvent(QCloseEvent *event)
{ {
if (!db->getLoadSuccess()) bool showLoadError = true;
if (QMessageBox::critical(this, tr("Error"), tr("Your card database is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { QString loadErrorMessage = tr("Unknown Error loading card database");
LoadStatus loadStatus = db->getLoadStatus();
qDebug() << "Card Database load status: " << loadStatus;
switch(loadStatus) {
case Ok:
showLoadError = false;
break;
case Invalid:
loadErrorMessage =
tr("Your card database is invalid.\n\n"
"Cockatrice may not function correctly with an invalid database\n\n"
"You may need to rerun oracle to update your card database.\n\n"
"Would you like to change your database location setting?");
break;
case VersionTooOld:
loadErrorMessage =
tr("Your card database version is too old.\n\n"
"This can cause problems loading card information or images\n\n"
"Usually this can be fixed by rerunning oracle to to update your card database.\n\n"
"Would you like to change your database location setting?");
break;
case NotLoaded:
loadErrorMessage =
tr("Your card database did not finish loading\n\n"
"Please file a ticket at http://github.com/Daenyth/Cockatrice/issues with your cards.xml attached\n\n"
"Would you like to change your database location setting?");
break;
case FileError:
loadErrorMessage =
tr("File Error loading your card database.\n\n"
"Would you like to change your database location setting?");
break;
case NoCards:
loadErrorMessage =
tr("Your card database was loaded but contains no cards.\n\n"
"Would you like to change your database location setting?");
break;
default:
loadErrorMessage =
tr("Unknown card database load status\n\n"
"Please file a ticket at http://github.com/Daenyth/Cockatrice/issues\n\n"
"Would you like to change your database location setting?");
break;
}
if (showLoadError)
if (QMessageBox::critical(this, tr("Error"), loadErrorMessage, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
event->ignore(); event->ignore();
return; return;
} }
if (!QDir(settingsCache->getDeckPath()).exists() || settingsCache->getDeckPath().isEmpty()) if (!QDir(settingsCache->getDeckPath()).exists() || settingsCache->getDeckPath().isEmpty())
// TODO: Prompt to create it
if (QMessageBox::critical(this, tr("Error"), tr("The path to your deck directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { if (QMessageBox::critical(this, tr("Error"), tr("The path to your deck directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
event->ignore(); event->ignore();
return; return;
} }
if (!QDir(settingsCache->getPicsPath()).exists() || settingsCache->getPicsPath().isEmpty()) if (!QDir(settingsCache->getPicsPath()).exists() || settingsCache->getPicsPath().isEmpty())
// TODO: Prompt to create it
if (QMessageBox::critical(this, tr("Error"), tr("The path to your card pictures directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { if (QMessageBox::critical(this, tr("Error"), tr("The path to your card pictures directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
event->ignore(); event->ignore();
return; return;

View file

@ -74,10 +74,18 @@ void installNewTranslator()
qApp->installTranslator(translator); qApp->installTranslator(translator);
} }
bool settingsValid()
{
return QDir(settingsCache->getDeckPath()).exists() &&
!settingsCache->getDeckPath().isEmpty() &&
QDir(settingsCache->getPicsPath()).exists() &&
!settingsCache->getPicsPath().isEmpty();
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QApplication app(argc, argv); QApplication app(argc, argv);
if (app.arguments().contains("--debug-output")) if (app.arguments().contains("--debug-output"))
qInstallMsgHandler(myMessageOutput); qInstallMsgHandler(myMessageOutput);
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
@ -97,7 +105,7 @@ int main(int argc, char *argv[])
QCoreApplication::setOrganizationName("Cockatrice"); QCoreApplication::setOrganizationName("Cockatrice");
QCoreApplication::setOrganizationDomain("cockatrice.de"); QCoreApplication::setOrganizationDomain("cockatrice.de");
QCoreApplication::setApplicationName("Cockatrice"); QCoreApplication::setApplicationName("Cockatrice");
if (translationPath.isEmpty()) { if (translationPath.isEmpty()) {
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
QDir translationsDir = baseDir; QDir translationsDir = baseDir;
@ -108,7 +116,7 @@ int main(int argc, char *argv[])
translationPath = app.applicationDirPath() + "/translations"; translationPath = app.applicationDirPath() + "/translations";
#endif #endif
} }
rng = new RNG_SFMT; rng = new RNG_SFMT;
settingsCache = new SettingsCache; settingsCache = new SettingsCache;
db = new CardDatabase; db = new CardDatabase;
@ -119,7 +127,6 @@ int main(int argc, char *argv[])
qsrand(QDateTime::currentDateTime().toTime_t()); qsrand(QDateTime::currentDateTime().toTime_t());
bool startMainProgram = true;
const QString dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation); const QString dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
if (!db->getLoadSuccess()) if (!db->getLoadSuccess())
if (db->loadCardDatabase(dataDir + "/cards.xml")) if (db->loadCardDatabase(dataDir + "/cards.xml"))
@ -138,30 +145,30 @@ int main(int argc, char *argv[])
QDir().mkpath(dataDir + "/pics"); QDir().mkpath(dataDir + "/pics");
settingsCache->setPicsPath(dataDir + "/pics"); settingsCache->setPicsPath(dataDir + "/pics");
} }
if (!db->getLoadSuccess() || !QDir(settingsCache->getDeckPath()).exists() || settingsCache->getDeckPath().isEmpty() || settingsCache->getPicsPath().isEmpty() || !QDir(settingsCache->getPicsPath()).exists()) { if (!settingsValid() || db->getLoadStatus() != Ok) {
qDebug("main(): invalid settings or load status");
DlgSettings dlgSettings; DlgSettings dlgSettings;
dlgSettings.show(); dlgSettings.show();
app.exec(); app.exec();
startMainProgram = (db->getLoadSuccess() && QDir(settingsCache->getDeckPath()).exists() && !settingsCache->getDeckPath().isEmpty() && QDir(settingsCache->getPicsPath()).exists() && !settingsCache->getPicsPath().isEmpty());
} }
if (startMainProgram) { if (settingsValid()) {
qDebug("main(): starting main program"); qDebug("main(): starting main program");
soundEngine = new SoundEngine; soundEngine = new SoundEngine;
qDebug("main(): SoundEngine constructor finished"); qDebug("main(): SoundEngine constructor finished");
MainWindow ui; MainWindow ui;
qDebug("main(): MainWindow constructor finished"); qDebug("main(): MainWindow constructor finished");
QIcon icon(":/resources/appicon.svg"); QIcon icon(":/resources/appicon.svg");
ui.setWindowIcon(icon); ui.setWindowIcon(icon);
ui.show(); ui.show();
qDebug("main(): ui.show() finished"); qDebug("main(): ui.show() finished");
app.exec(); app.exec();
} }
qDebug("Event loop finished, terminating..."); qDebug("Event loop finished, terminating...");
delete db; delete db;
delete settingsCache; delete settingsCache;
@ -169,6 +176,6 @@ int main(int argc, char *argv[])
PingPixmapGenerator::clear(); PingPixmapGenerator::clear();
CountryPixmapGenerator::clear(); CountryPixmapGenerator::clear();
UserLevelPixmapGenerator::clear(); UserLevelPixmapGenerator::clear();
return 0; return 0;
} }