From d5adec53a2becbe7dcd57535b1445288bf9bfe45 Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 23 Sep 2015 03:17:33 -0400 Subject: [PATCH] Revert "Reimplemented PictureLoader as a singleton" --- cockatrice/CMakeLists.txt | 1 - cockatrice/src/abstractcarditem.cpp | 12 +- cockatrice/src/carddatabase.cpp | 565 ++++++++++++++++++++++++++-- cockatrice/src/carddatabase.h | 88 ++++- cockatrice/src/cardframe.cpp | 3 +- cockatrice/src/cardinfopicture.cpp | 8 +- cockatrice/src/cardinfotext.cpp | 24 +- cockatrice/src/cardinfowidget.cpp | 5 +- cockatrice/src/carditem.cpp | 3 +- cockatrice/src/dlg_settings.cpp | 63 +--- cockatrice/src/dlg_settings.h | 13 +- cockatrice/src/pictureloader.cpp | 450 ---------------------- cockatrice/src/pictureloader.h | 78 ---- cockatrice/src/settingscache.cpp | 22 ++ cockatrice/src/settingscache.h | 12 + cockatrice/src/tab_deck_editor.cpp | 3 +- cockatrice/src/tab_game.cpp | 5 +- cockatrice/src/window_sets.cpp | 4 +- doc/cards.xsd | 1 + oracle/CMakeLists.txt | 1 - oracle/src/oracleimporter.h | 1 - 21 files changed, 705 insertions(+), 657 deletions(-) delete mode 100644 cockatrice/src/pictureloader.cpp delete mode 100644 cockatrice/src/pictureloader.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index ccfb3cf0..f9657ced 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -96,7 +96,6 @@ SET(cockatrice_SOURCES src/qt-json/json.cpp src/soundengine.cpp src/pending_command.cpp - src/pictureloader.cpp src/shortcutssettings.cpp src/sequenceEdit/sequenceedit.cpp src/sequenceEdit/shortcutstab.cpp diff --git a/cockatrice/src/abstractcarditem.cpp b/cockatrice/src/abstractcarditem.cpp index a177eabe..dcd68fa1 100644 --- a/cockatrice/src/abstractcarditem.cpp +++ b/cockatrice/src/abstractcarditem.cpp @@ -9,7 +9,6 @@ #include "carddatabase.h" #include "cardinfowidget.h" #include "abstractcarditem.h" -#include "pictureloader.h" #include "settingscache.h" #include "main.h" #include "gamescene.h" @@ -46,8 +45,7 @@ void AbstractCardItem::pixmapUpdated() void AbstractCardItem::cardInfoUpdated() { info = db->getCard(name); - if(info) - connect(info, SIGNAL(pixmapUpdated()), this, SLOT(pixmapUpdated())); + connect(info, SIGNAL(pixmapUpdated()), this, SLOT(pixmapUpdated())); } void AbstractCardItem::setRealZValue(qreal _zValue) @@ -95,7 +93,7 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS QPixmap translatedPixmap; // don't even spend time trying to load the picture if our size is too small if(translatedSize.width() > 10) - PictureLoader::getPixmap(translatedPixmap, imageSource, translatedSize.toSize()); + imageSource->getPixmap(translatedSize.toSize(), translatedPixmap); painter->save(); QColor bgColor = Qt::transparent; @@ -193,12 +191,10 @@ void AbstractCardItem::setName(const QString &_name) return; emit deleteCardInfoPopup(name); - if(info) - disconnect(info, 0, this, 0); + disconnect(info, 0, this, 0); name = _name; info = db->getCard(name); - if(info) - connect(info, SIGNAL(pixmapUpdated()), this, SLOT(pixmapUpdated())); + connect(info, SIGNAL(pixmapUpdated()), this, SLOT(pixmapUpdated())); update(); } diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index ae85bfc1..3e5a1489 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -1,16 +1,20 @@ #include "carddatabase.h" -#include "pictureloader.h" #include "settingscache.h" #include "thememanager.h" #include - -#include #include #include #include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include const int CardDatabase::versionNeeded = 3; @@ -84,6 +88,38 @@ void SetList::sortByKey() qSort(begin(), end(), KeyCompareFunctor()); } +class SetList::EnabledAndKeyCompareFunctor { +public: + inline bool operator()(CardSet *a, CardSet *b) const + { + if(a->getEnabled()) + { + if(b->getEnabled()) + { + // both enabled: sort by key + return a->getSortKey() < b->getSortKey(); + } else { + // only a enabled + return true; + } + } else { + if(b->getEnabled()) + { + // only b enabled + return false; + } else { + // both disabled: sort by key + return a->getSortKey() < b->getSortKey(); + } + } + } +}; + +void SetList::sortByEnabledAndKey() +{ + qSort(begin(), end(), EnabledAndKeyCompareFunctor()); +} + int SetList::getEnabledSetsNum() { int num=0; @@ -159,6 +195,334 @@ void SetList::guessSortKeys() } } +PictureToLoad::PictureToLoad(CardInfo *_card, bool _hq) + : card(_card), setIndex(0), hq(_hq) +{ + if (card) { + sortedSets = card->getSets(); + sortedSets.sortByEnabledAndKey(); + } +} + +bool PictureToLoad::nextSet() +{ + if (setIndex == sortedSets.size() - 1) + return false; + ++setIndex; + return true; +} + +QString PictureToLoad::getSetName() const +{ + if (setIndex < sortedSets.size()) + return sortedSets[setIndex]->getCorrectedShortName(); + else + return QString(""); +} + +CardSet *PictureToLoad::getCurrentSet() const +{ + if (setIndex < sortedSets.size()) + return sortedSets[setIndex]; + else + return 0; +} + +QStringList PictureLoader::md5Blacklist = QStringList() + << "db0c48db407a907c16ade38de048a441"; // card back returned by gatherer when card is not found + +PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent) + : QObject(parent), + _picsPath(__picsPath), picDownload(_picDownload), picDownloadHq(_picDownloadHq), + downloadRunning(false), loadQueueRunning(false) +{ + connect(this, SIGNAL(startLoadQueue()), this, SLOT(processLoadQueue()), Qt::QueuedConnection); + + networkManager = new QNetworkAccessManager(this); + connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(picDownloadFinished(QNetworkReply *))); +} + +PictureLoader::~PictureLoader() +{ + // This does not work with the destroyed() signal as this destructor is called after the main event loop is done. + thread()->quit(); +} + +void PictureLoader::processLoadQueue() +{ + if (loadQueueRunning) + return; + + loadQueueRunning = true; + forever { + mutex.lock(); + if (loadQueue.isEmpty()) { + mutex.unlock(); + loadQueueRunning = false; + return; + } + cardBeingLoaded = loadQueue.takeFirst(); + mutex.unlock(); + + QString setName = cardBeingLoaded.getSetName(); + QString correctedCardname = cardBeingLoaded.getCard()->getCorrectedName(); + qDebug() << "Trying to load picture (set: " << setName << " card: " << correctedCardname << ")"; + + //The list of paths to the folders in which to search for images + QList picsPaths = QList() << _picsPath + "/CUSTOM/" + correctedCardname; + + if(!setName.isEmpty()) + { + picsPaths << _picsPath + "/" + setName + "/" + correctedCardname + << _picsPath + "/downloadedPics/" + setName + "/" + correctedCardname; + } + + QImage image; + QImageReader imgReader; + imgReader.setDecideFormatFromContent(true); + 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)) { + qDebug() << "Picture found on disk (set: " << setName << " card: " << correctedCardname << ")"; + emit imageLoaded(cardBeingLoaded.getCard(), image); + found = true; + break; + } + imgReader.setFileName(picsPaths.at(i) + ".full"); + if (imgReader.read(&image)) { + qDebug() << "Picture.full found on disk (set: " << setName << " card: " << correctedCardname << ")"; + emit imageLoaded(cardBeingLoaded.getCard(), image); + found = true; + } + } + + if (!found) { + if (picDownload) { + qDebug() << "Picture NOT found, trying to download (set: " << setName << " card: " << correctedCardname << ")"; + cardsToDownload.append(cardBeingLoaded); + cardBeingLoaded=0; + if (!downloadRunning) + startNextPicDownload(); + } else { + if (cardBeingLoaded.nextSet()) + { + qDebug() << "Picture NOT found and download disabled, moving to next set (newset: " << setName << " card: " << correctedCardname << ")"; + mutex.lock(); + loadQueue.prepend(cardBeingLoaded); + cardBeingLoaded=0; + mutex.unlock(); + } else { + qDebug() << "Picture NOT found, download disabled, no more sets to try: BAILING OUT (oldset: " << setName << " card: " << correctedCardname << ")"; + emit imageLoaded(cardBeingLoaded.getCard(), QImage()); + } + } + } + } +} + +QString PictureLoader::getPicUrl() +{ + if (!picDownload) return QString(""); + + CardInfo *card = cardBeingDownloaded.getCard(); + CardSet *set=cardBeingDownloaded.getCurrentSet(); + 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; + } + + // if a card has a muid, use the default url; if not, use the fallback + int muid = set ? card->getMuId(set->getShortName()) : 0; + if(muid) + picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl(); + else + picUrl = picDownloadHq ? settingsCache->getPicUrlHqFallback() : settingsCache->getPicUrlFallback(); + + picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName())); + picUrl.replace("!name_lower!", QUrl::toPercentEncoding(card->getCorrectedName().toLower())); + picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(muid))); + if (set) + { + picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName())); + picUrl.replace("!setcode_lower!", QUrl::toPercentEncoding(set->getShortName().toLower())); + picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName())); + picUrl.replace("!setname_lower!", QUrl::toPercentEncoding(set->getLongName().toLower())); + } + + if ( + picUrl.contains("!name!") || + picUrl.contains("!name_lower!") || + picUrl.contains("!setcode!") || + picUrl.contains("!setcode_lower!") || + picUrl.contains("!setname!") || + picUrl.contains("!setname_lower!") || + picUrl.contains("!cardid!") + ) + { + qDebug() << "Insufficient card data to download" << card->getName() << "Url:" << picUrl; + return QString(""); + } + + return picUrl; +} + +void PictureLoader::startNextPicDownload() +{ + if (cardsToDownload.isEmpty()) { + cardBeingDownloaded = 0; + downloadRunning = false; + return; + } + + downloadRunning = true; + + cardBeingDownloaded = cardsToDownload.takeFirst(); + + QString picUrl = getPicUrl(); + if (picUrl.isEmpty()) { + downloadRunning = false; + picDownloadFailed(); + } else { + QUrl url(picUrl); + + QNetworkRequest req(url); + qDebug() << "starting picture download:" << cardBeingDownloaded.getCard()->getName() << "Url:" << req.url(); + networkManager->get(req); + } +} + +void PictureLoader::picDownloadFailed() +{ + if (cardBeingDownloaded.nextSet()) + { + qDebug() << "Picture NOT found, download failed, moving to next set (newset: " << cardBeingDownloaded.getSetName() << " card: " << cardBeingDownloaded.getCard()->getCorrectedName() << ")"; + mutex.lock(); + loadQueue.prepend(cardBeingDownloaded); + mutex.unlock(); + emit startLoadQueue(); + } else { + qDebug() << "Picture NOT found, download failed, no more sets to try: BAILING OUT (oldset: " << cardBeingDownloaded.getSetName() << " card: " << cardBeingDownloaded.getCard()->getCorrectedName() << ")"; + cardBeingDownloaded = 0; + emit imageLoaded(cardBeingDownloaded.getCard(), QImage()); + } +} + +void PictureLoader::picDownloadFinished(QNetworkReply *reply) +{ + QString picsPath = _picsPath; + if (reply->error()) { + qDebug() << "Download failed:" << reply->errorString(); + } + + int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (statusCode == 301 || statusCode == 302) { + QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + QNetworkRequest req(redirectUrl); + qDebug() << "following redirect:" << cardBeingDownloaded.getCard()->getName() << "Url:" << req.url(); + networkManager->get(req); + return; + } + + const QByteArray &picData = reply->peek(reply->size()); //peek is used to keep the data in the buffer for use by QImageReader + + // check if the image is blacklisted + QString md5sum = QCryptographicHash::hash(picData, QCryptographicHash::Md5).toHex(); + if(md5Blacklist.contains(md5sum)) + { + qDebug() << "Picture downloaded, but blacklisted (" << md5sum << "), will consider it as not found"; + picDownloadFailed(); + reply->deleteLater(); + startNextPicDownload(); + return; + } + + QImage testImage; + + QImageReader imgReader; + imgReader.setDecideFormatFromContent(true); + 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; + } + + QFile newPic(picsPath + "/downloadedPics/" + setName + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + extension); + if (!newPic.open(QIODevice::WriteOnly)) + return; + newPic.write(picData); + newPic.close(); + } + + emit imageLoaded(cardBeingDownloaded.getCard(), testImage); + } else { + picDownloadFailed(); + } + + reply->deleteLater(); + startNextPicDownload(); +} + +void PictureLoader::loadImage(CardInfo *card) +{ + QMutexLocker locker(&mutex); + + // avoid queueing the same card more than once + if(card == cardBeingLoaded.getCard() || card == cardBeingDownloaded.getCard()) + return; + + foreach(PictureToLoad pic, loadQueue) + { + if(pic.getCard() == card) + return; + } + + loadQueue.append(PictureToLoad(card)); + emit startLoadQueue(); +} + +void PictureLoader::setPicsPath(const QString &path) +{ + QMutexLocker locker(&mutex); + _picsPath = path; +} + +void PictureLoader::setPicDownload(bool _picDownload) +{ + QMutexLocker locker(&mutex); + picDownload = _picDownload; +} + +void PictureLoader::setPicDownloadHq(bool _picDownloadHq) +{ + QMutexLocker locker(&mutex); + picDownloadHq = _picDownloadHq; +} + CardInfo::CardInfo(CardDatabase *_db, const QString &_name, bool _isToken, @@ -175,6 +539,7 @@ CardInfo::CardInfo(CardDatabase *_db, int _tableRow, const SetList &_sets, const QStringMap &_customPicURLs, + const QStringMap &_customPicURLsHq, MuidMap _muIds ) : db(_db), @@ -191,6 +556,7 @@ CardInfo::CardInfo(CardDatabase *_db, upsideDownArt(_upsideDownArt), loyalty(_loyalty), customPicURLs(_customPicURLs), + customPicURLsHq(_customPicURLsHq), muIds(_muIds), cipt(_cipt), tableRow(_tableRow) @@ -204,7 +570,7 @@ CardInfo::CardInfo(CardDatabase *_db, CardInfo::~CardInfo() { - PictureLoader::clearPixmapCache(this); + clearPixmapCache(); } QString CardInfo::getMainCardType() const @@ -251,6 +617,82 @@ void CardInfo::addToSet(CardSet *set) sets << set; } +void CardInfo::loadPixmap(QPixmap &pixmap) +{ + if(QPixmapCache::find(pixmapCacheKey, &pixmap)) + return; + + pixmap = QPixmap(); + + if (getName().isEmpty()) { + pixmap = QPixmap("theme:cardback"); + return; + } + + db->loadImage(this); +} + +void CardInfo::imageLoaded(const QImage &image) +{ + if (!image.isNull()) { + if(upsideDownArt) + { + QImage mirrorImage = image.mirrored(true, true); + QPixmapCache::insert(pixmapCacheKey, QPixmap::fromImage(mirrorImage)); + } else { + QPixmapCache::insert(pixmapCacheKey, QPixmap::fromImage(image)); + } + emit pixmapUpdated(); + } +} + +void CardInfo::getPixmap(QSize size, QPixmap &pixmap) +{ + QString key = QLatin1String("card_") + name + QLatin1Char('_') + QString::number(size.width()) + QString::number(size.height()); + if(QPixmapCache::find(key, &pixmap)) + return; + + QPixmap bigPixmap; + loadPixmap(bigPixmap); + if (bigPixmap.isNull()) { + if (getName().isEmpty()) { + pixmap = pixmap = QPixmap("theme:cardback"); + } else { + pixmap = QPixmap(); // null + return; + } + } + + pixmap = bigPixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); + QPixmapCache::insert(key, pixmap); +} + +void CardInfo::clearPixmapCache() +{ + //qDebug() << "Deleting pixmap for" << name; + QPixmapCache::remove(pixmapCacheKey); +} + +void CardInfo::clearPixmapCacheMiss() +{ + QPixmap pixmap; + if(!QPixmapCache::find(pixmapCacheKey, &pixmap)) + return; + + if (pixmap.isNull()) + clearPixmapCache(); +} + +void CardInfo::updatePixmapCache() +{ + qDebug() << "Updating pixmap cache for" << name; + clearPixmapCache(); + QPixmap tmp; + loadPixmap(tmp); + + emit pixmapUpdated(); +} + QString CardInfo::simplifyName(const QString &name) { QString simpleName(name); @@ -289,6 +731,10 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) if(!tmpString.isEmpty()) xml.writeAttribute("picURL", tmpString); + tmpString = info->getCustomPicURLHq(tmpSet); + if(!tmpString.isEmpty()) + xml.writeAttribute("picURLHq", tmpString); + xml.writeCharacters(tmpSet); xml.writeEndElement(); } @@ -321,18 +767,37 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) } CardDatabase::CardDatabase(QObject *parent) - : QObject(parent), loadStatus(NotLoaded) + : QObject(parent), noCard(0), loadStatus(NotLoaded) { + connect(settingsCache, SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged())); connect(settingsCache, SIGNAL(cardDatabasePathChanged()), this, SLOT(loadCardDatabase())); connect(settingsCache, SIGNAL(tokenDatabasePathChanged()), this, SLOT(loadTokenDatabase())); + connect(settingsCache, SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged())); + connect(settingsCache, SIGNAL(picDownloadHqChanged()), this, SLOT(picDownloadHqChanged())); loadCardDatabase(); loadTokenDatabase(); + + pictureLoaderThread = new QThread; + pictureLoader = new PictureLoader(settingsCache->getPicsPath(), settingsCache->getPicDownload(), settingsCache->getPicDownloadHq()); + pictureLoader->moveToThread(pictureLoaderThread); + connect(pictureLoader, SIGNAL(imageLoaded(CardInfo *, const QImage &)), this, SLOT(imageLoaded(CardInfo *, const QImage &))); + pictureLoaderThread->start(QThread::LowPriority); + + noCard = new CardInfo(this); + QPixmap tmp; + noCard->loadPixmap(tmp); // cache pixmap for card back + connect(themeManager, SIGNAL(themeChanged()), noCard, SLOT(updatePixmapCache())); } CardDatabase::~CardDatabase() { clear(); + delete noCard; + + pictureLoader->deleteLater(); + pictureLoaderThread->wait(); + delete pictureLoaderThread; } void CardDatabase::clear() @@ -374,15 +839,6 @@ CardInfo *CardDatabase::getCard(const QString &cardName, bool createIfNotFound) return getCardFromMap(cards, cardName, createIfNotFound); } -QList CardDatabase::getCards(const QStringList &cardNames) -{ - QList cardInfos; - foreach(QString cardName, cardNames) - cardInfos.append(getCardFromMap(cards, cardName, false)); - - return cardInfos; -} - CardInfo *CardDatabase::getCardBySimpleName(const QString &cardName, bool createIfNotFound) { QString simpleName = CardInfo::simplifyName(cardName); return getCardFromMap(simpleNameCards, simpleName, createIfNotFound); @@ -410,6 +866,21 @@ SetList CardDatabase::getSetList() const return result; } +void CardDatabase::clearPixmapCache() +{ + // This also clears the cards in simpleNameCards since they point to the + // same object. + QHashIterator i(cards); + while (i.hasNext()) { + i.next(); + i.value()->clearPixmapCache(); + } + if (noCard) + noCard->clearPixmapCache(); + + QPixmapCache::clear(); +} + void CardDatabase::loadSetsFromXml(QXmlStreamReader &xml) { while (!xml.atEnd()) { @@ -447,7 +918,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) if (xml.name() == "card") { QString name, manacost, cmc, type, pt, text; QStringList colors, relatedCards; - QStringMap customPicURLs; + QStringMap customPicURLs, customPicURLsHq; MuidMap muids; SetList sets; int tableRow = 0; @@ -480,6 +951,9 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) 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") colors << xml.readElementText(); else if (xml.name() == "related") @@ -497,24 +971,24 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) } if (isToken == tokens) { - addCard(new CardInfo(this, name, isToken, manacost, cmc, type, pt, text, colors, relatedCards, upsideDown, loyalty, cipt, tableRow, sets, customPicURLs, muids)); + addCard(new CardInfo(this, name, isToken, manacost, cmc, type, pt, text, colors, relatedCards, upsideDown, loyalty, cipt, tableRow, sets, customPicURLs, customPicURLsHq, muids)); } } } } CardInfo *CardDatabase::getCardFromMap(CardNameMap &cardMap, const QString &cardName, bool createIfNotFound) { - if (cardMap.contains(cardName)) + if (cardName.isEmpty()) + return noCard; + else if (cardMap.contains(cardName)) return cardMap.value(cardName); - - if (createIfNotFound) { + else if (createIfNotFound) { CardInfo *newCard = new CardInfo(this, cardName, true); newCard->addToSet(getSet(CardDatabase::TOKENS_SETNAME)); cardMap.insert(cardName, newCard); return newCard; - } - - return 0; + } else + return 0; } LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens) @@ -587,6 +1061,26 @@ bool CardDatabase::saveToFile(const QString &fileName, bool tokens) return true; } +void CardDatabase::picDownloadChanged() +{ + pictureLoader->setPicDownload(settingsCache->getPicDownload()); + if (settingsCache->getPicDownload()) { + QHashIterator cardIterator(cards); + while (cardIterator.hasNext()) + cardIterator.next().value()->clearPixmapCacheMiss(); + } +} + +void CardDatabase::picDownloadHqChanged() +{ + pictureLoader->setPicDownloadHq(settingsCache->getPicDownloadHq()); + if (settingsCache->getPicDownloadHq()) { + QHashIterator cardIterator(cards); + while (cardIterator.hasNext()) + cardIterator.next().value()->clearPixmapCacheMiss(); + } +} + void CardDatabase::emitCardListChanged() { emit cardListChanged(); @@ -665,6 +1159,31 @@ QStringList CardDatabase::getAllMainCardTypes() const return types.toList(); } +void CardDatabase::cacheCardPixmaps(const QStringList &cardNames) +{ + QPixmap tmp; + // never cache more than 300 cards at once for a single deck + int max = qMin(cardNames.size(), 300); + for (int i = 0; i < max; ++i) + getCard(cardNames[i])->loadPixmap(tmp); +} + +void CardDatabase::loadImage(CardInfo *card) +{ + pictureLoader->loadImage(card); +} + +void CardDatabase::imageLoaded(CardInfo *card, QImage image) +{ + card->imageLoaded(image); +} + +void CardDatabase::picsPathChanged() +{ + pictureLoader->setPicsPath(settingsCache->getPicsPath()); + clearPixmapCache(); +} + void CardDatabase::checkUnknownSets() { SetList sets = getSetList(); diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index 096bbc8f..df3995da 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -8,9 +8,17 @@ #include #include #include +#include +#include +#include +#include +#include class CardDatabase; class CardInfo; +class QNetworkAccessManager; +class QNetworkReply; +class QNetworkRequest; typedef QMap QStringMap; @@ -47,7 +55,9 @@ public: class SetList : public QList { private: class KeyCompareFunctor; + class EnabledAndKeyCompareFunctor; public: + void sortByEnabledAndKey(); void sortByKey(); void guessSortKeys(); void enableAllUnknown(); @@ -57,6 +67,53 @@ public: int getUnknownSetsNum(); }; +class PictureToLoad { +private: + CardInfo *card; + SetList sortedSets; + int setIndex; + bool hq; +public: + PictureToLoad(CardInfo *_card = 0, bool _hq = true); + CardInfo *getCard() const { return card; } + CardSet *getCurrentSet() const; + QString getSetName() const; + bool nextSet(); + bool getHq() const { return hq; } + void setHq(bool _hq) { hq = _hq; } +}; + +class PictureLoader : public QObject { + Q_OBJECT +private: + QString _picsPath; + QList loadQueue; + QMutex mutex; + QNetworkAccessManager *networkManager; + QList cardsToDownload; + PictureToLoad cardBeingLoaded; + PictureToLoad cardBeingDownloaded; + bool picDownload, picDownloadHq, downloadRunning, loadQueueRunning; + void startNextPicDownload(); + QString getPicUrl(); + static QStringList md5Blacklist; +public: + PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent = 0); + ~PictureLoader(); + void setPicsPath(const QString &path); + void setPicDownload(bool _picDownload); + void setPicDownloadHq(bool _picDownloadHq); + void loadImage(CardInfo *card); +private slots: + void picDownloadFinished(QNetworkReply *reply); + void picDownloadFailed(); +public slots: + void processLoadQueue(); +signals: + void startLoadQueue(); + void imageLoaded(CardInfo *card, const QImage &image); +}; + class CardInfo : public QObject { Q_OBJECT private: @@ -81,7 +138,7 @@ private: QStringList relatedCards; bool upsideDownArt; int loyalty; - QStringMap customPicURLs; + QStringMap customPicURLs, customPicURLsHq; MuidMap muIds; bool cipt; int tableRow; @@ -103,6 +160,7 @@ public: int _tableRow = 0, const SetList &_sets = SetList(), const QStringMap &_customPicURLs = QStringMap(), + const QStringMap &_customPicURLsHq = QStringMap(), MuidMap muids = MuidMap() ); ~CardInfo(); @@ -115,7 +173,6 @@ public: const QString &getCardType() const { return cardtype; } const QString &getPowTough() const { return powtough; } const QString &getText() const { return text; } - const QString &getPixmapCacheKey() const { return pixmapCacheKey; } const int &getLoyalty() const { return loyalty; } bool getCipt() const { return cipt; } void setManaCost(const QString &_manaCost) { manacost = _manaCost; emit cardInfoChanged(this); } @@ -128,6 +185,7 @@ public: const QStringList &getRelatedCards() const { return relatedCards; } bool getUpsideDownArt() const { return upsideDownArt; } 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); } QString getMainCardType() const; QString getCorrectedName() const; @@ -135,15 +193,22 @@ public: void setTableRow(int _tableRow) { tableRow = _tableRow; } void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(this); } void setCustomPicURL(const QString &_set, const QString &_customPicURL) { customPicURLs.insert(_set, _customPicURL); } + void setCustomPicURLHq(const QString &_set, const QString &_customPicURL) { customPicURLsHq.insert(_set, _customPicURL); } void setMuId(const QString &_set, const int &_muId) { muIds.insert(_set, _muId); } void addToSet(CardSet *set); - void emitPixmapUpdated() { emit pixmapUpdated(); } + void loadPixmap(QPixmap &pixmap); + void getPixmap(QSize size, QPixmap &pixmap); + void clearPixmapCache(); + void clearPixmapCacheMiss(); + void imageLoaded(const QImage &image); /** * Simplify a name to have no punctuation and lowercase all letters, for * less strict name-matching. */ static QString simplifyName(const QString &name); +public slots: + void updatePixmapCache(); signals: void pixmapUpdated(); void cardInfoChanged(CardInfo *card); @@ -172,6 +237,10 @@ protected: */ SetNameMap sets; + CardInfo *noCard; + + QThread *pictureLoaderThread; + PictureLoader *pictureLoader; LoadStatus loadStatus; bool detectedFirstRun; private: @@ -189,14 +258,13 @@ public: void clear(); void addCard(CardInfo *card); void removeCard(CardInfo *card); - CardInfo *getCard(const QString &cardName = QString(), bool createIfNotFound = false); - QList getCards(const QStringList &cardNames); + 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 = false); + CardInfo *getCardBySimpleName(const QString &cardName = QString(), bool createIfNotFound = true); CardSet *getSet(const QString &setName); QList getCardList() const { return cards.values(); } @@ -207,12 +275,20 @@ public: QStringList getAllMainCardTypes() const; LoadStatus getLoadStatus() const { return loadStatus; } bool getLoadSuccess() const { return loadStatus == Ok; } + void cacheCardPixmaps(const QStringList &cardNames); + void loadImage(CardInfo *card); bool hasDetectedFirstRun(); public slots: + void clearPixmapCache(); LoadStatus loadCardDatabase(const QString &path, bool tokens = false); void loadCustomCardDatabases(const QString &path); void emitCardListChanged(); private slots: + void imageLoaded(CardInfo *card, QImage image); + void picDownloadChanged(); + void picDownloadHqChanged(); + void picsPathChanged(); + void loadCardDatabase(); void loadTokenDatabase(); signals: diff --git a/cockatrice/src/cardframe.cpp b/cockatrice/src/cardframe.cpp index c0b92041..d91a8ecf 100644 --- a/cockatrice/src/cardframe.cpp +++ b/cockatrice/src/cardframe.cpp @@ -93,8 +93,7 @@ void CardFrame::setCard(CardInfo *card) if (info) disconnect(info, 0, this, 0); info = card; - if(info) - connect(info, SIGNAL(destroyed()), this, SLOT(clear())); + connect(info, SIGNAL(destroyed()), this, SLOT(clear())); text->setCard(info); pic->setCard(info); } diff --git a/cockatrice/src/cardinfopicture.cpp b/cockatrice/src/cardinfopicture.cpp index 10ce0978..9bce273e 100644 --- a/cockatrice/src/cardinfopicture.cpp +++ b/cockatrice/src/cardinfopicture.cpp @@ -6,7 +6,6 @@ #include "carditem.h" #include "carddatabase.h" -#include "pictureloader.h" #include "main.h" CardInfoPicture::CardInfoPicture(QWidget *parent) @@ -22,8 +21,7 @@ void CardInfoPicture::setCard(CardInfo *card) if (info) disconnect(info, 0, this, 0); info = card; - if(info) - connect(info, SIGNAL(pixmapUpdated()), this, SLOT(updatePixmap())); + connect(info, SIGNAL(pixmapUpdated()), this, SLOT(updatePixmap())); updatePixmap(); } @@ -42,13 +40,13 @@ void CardInfoPicture::updatePixmap() void CardInfoPicture::loadPixmap() { if(info) - PictureLoader::getPixmap(resizedPixmap, info, size()); + info->getPixmap(size(), resizedPixmap); else resizedPixmap = QPixmap(); if (resizedPixmap.isNull()) - PictureLoader::getPixmap(resizedPixmap, db->getCard(), size()); + db->getCard()->getPixmap(size(), resizedPixmap); } void CardInfoPicture::paintEvent(QPaintEvent *) diff --git a/cockatrice/src/cardinfotext.cpp b/cockatrice/src/cardinfotext.cpp index ce6524b8..ace2a95f 100644 --- a/cockatrice/src/cardinfotext.cpp +++ b/cockatrice/src/cardinfotext.cpp @@ -53,23 +53,13 @@ CardInfoText::CardInfoText(QWidget *parent) void CardInfoText::setCard(CardInfo *card) { - if(card) { - nameLabel2->setText(card->getName()); - manacostLabel2->setText(card->getManaCost()); - colorLabel2->setText(card->getColors().join("")); - cardtypeLabel2->setText(card->getCardType()); - powtoughLabel2->setText(card->getPowTough()); - loyaltyLabel2->setText(card->getLoyalty() > 0 ? QString::number(card->getLoyalty()) : QString()); - textLabel->setText(card->getText()); - } else { - nameLabel2->setText(""); - manacostLabel2->setText(""); - colorLabel2->setText(""); - cardtypeLabel2->setText(""); - powtoughLabel2->setText(""); - loyaltyLabel2->setText(""); - textLabel->setText(""); - } + nameLabel2->setText(card->getName()); + manacostLabel2->setText(card->getManaCost()); + colorLabel2->setText(card->getColors().join("")); + cardtypeLabel2->setText(card->getCardType()); + powtoughLabel2->setText(card->getPowTough()); + loyaltyLabel2->setText(card->getLoyalty() > 0 ? QString::number(card->getLoyalty()) : QString()); + textLabel->setText(card->getText()); } void CardInfoText::retranslateUi() diff --git a/cockatrice/src/cardinfowidget.cpp b/cockatrice/src/cardinfowidget.cpp index 807221c6..8ee5663c 100644 --- a/cockatrice/src/cardinfowidget.cpp +++ b/cockatrice/src/cardinfowidget.cpp @@ -8,7 +8,6 @@ #include "cardinfowidget.h" #include "carditem.h" #include "carddatabase.h" -#include "pictureloader.h" #include "main.h" #include "settingscache.h" @@ -196,10 +195,10 @@ void CardInfoWidget::updatePixmap() return; QPixmap resizedPixmap; - PictureLoader::getPixmap(resizedPixmap, info, QSize(pixmapWidth, pixmapWidth * aspectRatio)); + info->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio), resizedPixmap); if (resizedPixmap.isNull()) - PictureLoader::getPixmap(resizedPixmap, getCard(), QSize(pixmapWidth, pixmapWidth * aspectRatio)); + getCard()->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio), resizedPixmap); cardPicture->setPixmap(resizedPixmap); } diff --git a/cockatrice/src/carditem.cpp b/cockatrice/src/carditem.cpp index 50870ddc..0ae87ee8 100644 --- a/cockatrice/src/carditem.cpp +++ b/cockatrice/src/carditem.cpp @@ -107,8 +107,7 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, painter->save(); transformPainter(painter, translatedSize, tapAngle); - CardInfo *card = db->getCard(name); - QStringList ptDbSplit = card ? card->getPowTough().split("/") : QStringList(); + QStringList ptDbSplit = db->getCard(name)->getPowTough().split("/"); QStringList ptSplit = pt.split("/"); if (getFaceDown() || ptDbSplit.at(0) != ptSplit.at(0) || ptDbSplit.at(1) != ptSplit.at(1)) diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index 6c3b982a..8f84001f 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -44,6 +44,7 @@ GeneralSettingsPage::GeneralSettingsPage() } picDownloadCheckBox.setChecked(settingsCache->getPicDownload()); + picDownloadHqCheckBox.setChecked(settingsCache->getPicDownloadHq()); updateNotificationCheckBox.setChecked(settingsCache->getNotifyAboutUpdates()); pixmapCacheEdit.setMinimum(PIXMAPCACHE_SIZE_MIN); @@ -52,22 +53,20 @@ GeneralSettingsPage::GeneralSettingsPage() pixmapCacheEdit.setSingleStep(64); pixmapCacheEdit.setValue(settingsCache->getPixmapCacheSize()); pixmapCacheEdit.setSuffix(" MB"); + picDownloadHqCheckBox.setChecked(settingsCache->getPicDownloadHq()); + picDownloadCheckBox.setChecked(settingsCache->getPicDownload()); - defaultUrlEdit = new QLineEdit(settingsCache->getPicUrl()); - fallbackUrlEdit = new QLineEdit(settingsCache->getPicUrlFallback()); + highQualityURLEdit = new QLineEdit(settingsCache->getPicUrlHq()); + highQualityURLEdit->setEnabled(settingsCache->getPicDownloadHq()); connect(&clearDownloadedPicsButton, SIGNAL(clicked()), this, SLOT(clearDownloadedPicsButtonClicked())); connect(&languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int))); connect(&picDownloadCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownload(int))); + connect(&picDownloadHqCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownloadHq(int))); connect(&pixmapCacheEdit, SIGNAL(valueChanged(int)), settingsCache, SLOT(setPixmapCacheSize(int))); + connect(&picDownloadHqCheckBox, SIGNAL(clicked(bool)), this, SLOT(setEnabledStatus(bool))); connect(&updateNotificationCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setNotifyAboutUpdate(int))); - connect(&picDownloadCheckBox, SIGNAL(clicked(bool)), this, SLOT(setEnabledStatus(bool))); - connect(defaultUrlEdit, SIGNAL(textChanged(QString)), settingsCache, SLOT(setPicUrl(QString))); - connect(fallbackUrlEdit, SIGNAL(textChanged(QString)), settingsCache, SLOT(setPicUrlFallback(QString))); - connect(&defaultUrlRestoreButton, SIGNAL(clicked()), this, SLOT(defaultUrlRestoreButtonClicked())); - connect(&fallbackUrlRestoreButton, SIGNAL(clicked()), this, SLOT(fallbackUrlRestoreButtonClicked())); - - setEnabledStatus(settingsCache->getPicDownload()); + connect(highQualityURLEdit, SIGNAL(textChanged(QString)), settingsCache, SLOT(setPicUrlHq(QString))); QGridLayout *personalGrid = new QGridLayout; personalGrid->addWidget(&languageLabel, 0, 0); @@ -75,18 +74,15 @@ GeneralSettingsPage::GeneralSettingsPage() personalGrid->addWidget(&pixmapCacheLabel, 1, 0); personalGrid->addWidget(&pixmapCacheEdit, 1, 1); personalGrid->addWidget(&updateNotificationCheckBox, 2, 0); - personalGrid->addWidget(&picDownloadCheckBox, 3, 0, 1, 3); - personalGrid->addWidget(&defaultUrlLabel, 4, 0, 1, 1); - personalGrid->addWidget(defaultUrlEdit, 4, 1, 1, 1); - personalGrid->addWidget(&defaultUrlRestoreButton, 4, 2, 1, 1); - personalGrid->addWidget(&fallbackUrlLabel, 5, 0, 1, 1); - personalGrid->addWidget(fallbackUrlEdit, 5, 1, 1, 1); - personalGrid->addWidget(&fallbackUrlRestoreButton, 5, 2, 1, 1); - personalGrid->addWidget(&urlLinkLabel, 6, 1, 1, 1); - personalGrid->addWidget(&clearDownloadedPicsButton, 7, 0, 1, 3); + personalGrid->addWidget(&picDownloadCheckBox, 3, 0); + personalGrid->addWidget(&picDownloadHqCheckBox, 4, 0); + personalGrid->addWidget(&highQualityURLLabel, 5, 0); + personalGrid->addWidget(highQualityURLEdit, 5, 1); + personalGrid->addWidget(&highQualityURLLinkLabel, 6, 1); + personalGrid->addWidget(&clearDownloadedPicsButton, 6, 0); - urlLinkLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); - urlLinkLabel.setOpenExternalLinks(true); + highQualityURLLinkLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); + highQualityURLLinkLabel.setOpenExternalLinks(true); personalGroupBox = new QGroupBox; personalGroupBox->setLayout(personalGrid); @@ -158,20 +154,6 @@ QString GeneralSettingsPage::languageName(const QString &qmFile) return translator.translate("GeneralSettingsPage", "English"); } -void GeneralSettingsPage::defaultUrlRestoreButtonClicked() -{ - QString path = PIC_URL_DEFAULT; - defaultUrlEdit->setText(path); - settingsCache->setPicUrl(path); -} - -void GeneralSettingsPage::fallbackUrlRestoreButtonClicked() -{ - QString path = PIC_URL_FALLBACK; - fallbackUrlEdit->setText(path); - settingsCache->setPicUrlFallback(path); -} - void GeneralSettingsPage::deckPathButtonClicked() { QString path = QFileDialog::getExistingDirectory(this, tr("Choose path")); @@ -256,6 +238,7 @@ void GeneralSettingsPage::retranslateUi() personalGroupBox->setTitle(tr("Personal settings")); languageLabel.setText(tr("Language:")); picDownloadCheckBox.setText(tr("Download card pictures on the fly")); + picDownloadHqCheckBox.setText(tr("Download card pictures from a custom URL")); pathsGroupBox->setTitle(tr("Paths")); deckPathLabel.setText(tr("Decks directory:")); replaysPathLabel.setText(tr("Replays directory:")); @@ -263,21 +246,15 @@ void GeneralSettingsPage::retranslateUi() cardDatabasePathLabel.setText(tr("Card database:")); tokenDatabasePathLabel.setText(tr("Token database:")); pixmapCacheLabel.setText(tr("Picture cache size:")); - defaultUrlLabel.setText(tr("Primary download URL:")); - fallbackUrlLabel.setText(tr("Fallback download URL:")); - urlLinkLabel.setText(QString("%2").arg(LINKING_FAQ_URL).arg(tr("Linking FAQ"))); + highQualityURLLabel.setText(tr("Custom Card Download URL:")); + highQualityURLLinkLabel.setText(QString("%2").arg(LINKING_FAQ_URL).arg(tr("Linking FAQ"))); clearDownloadedPicsButton.setText(tr("Reset/Clear Downloaded Pictures")); updateNotificationCheckBox.setText(tr("Notify when new client features are available")); - defaultUrlRestoreButton.setText(tr("Reset")); - fallbackUrlRestoreButton.setText(tr("Reset")); } void GeneralSettingsPage::setEnabledStatus(bool status) { - defaultUrlEdit->setEnabled(status); - fallbackUrlEdit->setEnabled(status); - defaultUrlRestoreButton.setEnabled(status); - fallbackUrlRestoreButton.setEnabled(status); + highQualityURLEdit->setEnabled(status); } AppearanceSettingsPage::AppearanceSettingsPage() diff --git a/cockatrice/src/dlg_settings.h b/cockatrice/src/dlg_settings.h index 52d3c9ba..7b746564 100644 --- a/cockatrice/src/dlg_settings.h +++ b/cockatrice/src/dlg_settings.h @@ -45,8 +45,6 @@ private slots: void tokenDatabasePathButtonClicked(); void languageBoxChanged(int index); void setEnabledStatus(bool); - void defaultUrlRestoreButtonClicked(); - void fallbackUrlRestoreButtonClicked(); private: QStringList findQmFiles(); QString languageName(const QString &qmFile); @@ -55,13 +53,13 @@ private: QLineEdit *picsPathEdit; QLineEdit *cardDatabasePathEdit; QLineEdit *tokenDatabasePathEdit; - QLineEdit *defaultUrlEdit; - QLineEdit *fallbackUrlEdit; + QLineEdit *highQualityURLEdit; QSpinBox pixmapCacheEdit; QGroupBox *personalGroupBox; QGroupBox *pathsGroupBox; QComboBox languageBox; QCheckBox picDownloadCheckBox; + QCheckBox picDownloadHqCheckBox; QCheckBox updateNotificationCheckBox; QLabel languageLabel; QLabel pixmapCacheLabel; @@ -70,12 +68,9 @@ private: QLabel picsPathLabel; QLabel cardDatabasePathLabel; QLabel tokenDatabasePathLabel; - QLabel defaultUrlLabel; - QLabel fallbackUrlLabel; - QLabel urlLinkLabel; + QLabel highQualityURLLabel; + QLabel highQualityURLLinkLabel; QPushButton clearDownloadedPicsButton; - QPushButton defaultUrlRestoreButton; - QPushButton fallbackUrlRestoreButton; }; class AppearanceSettingsPage : public AbstractSettingsPage { diff --git a/cockatrice/src/pictureloader.cpp b/cockatrice/src/pictureloader.cpp deleted file mode 100644 index 2b81c502..00000000 --- a/cockatrice/src/pictureloader.cpp +++ /dev/null @@ -1,450 +0,0 @@ -#include "pictureloader.h" -#include "carddatabase.h" -#include "main.h" -#include "settingscache.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class PictureToLoad::EnabledAndKeyCompareFunctor { -public: - inline bool operator()(CardSet *a, CardSet *b) const - { - if(a->getEnabled()) - { - if(b->getEnabled()) - { - // both enabled: sort by key - return a->getSortKey() < b->getSortKey(); - } else { - // only a enabled - return true; - } - } else { - if(b->getEnabled()) - { - // only b enabled - return false; - } else { - // both disabled: sort by key - return a->getSortKey() < b->getSortKey(); - } - } - } -}; - -PictureToLoad::PictureToLoad(CardInfo *_card) - : card(_card), setIndex(0) -{ - if (card) { - sortedSets = card->getSets(); - qSort(sortedSets.begin(), sortedSets.end(), EnabledAndKeyCompareFunctor()); - } -} - -bool PictureToLoad::nextSet() -{ - if (setIndex == sortedSets.size() - 1) - return false; - ++setIndex; - return true; -} - -QString PictureToLoad::getSetName() const -{ - if (setIndex < sortedSets.size()) - return sortedSets[setIndex]->getCorrectedShortName(); - else - return QString(""); -} - -CardSet *PictureToLoad::getCurrentSet() const -{ - if (setIndex < sortedSets.size()) - return sortedSets[setIndex]; - else - return 0; -} - -QStringList PictureLoader::md5Blacklist = QStringList() - << "db0c48db407a907c16ade38de048a441"; // card back returned by gatherer when card is not found - -PictureLoader::PictureLoader() - : QObject(0), - downloadRunning(false), loadQueueRunning(false) -{ - picsPath = settingsCache->getPicsPath(); - picDownload = settingsCache->getPicDownload(); - - connect(this, SIGNAL(startLoadQueue()), this, SLOT(processLoadQueue()), Qt::QueuedConnection); - connect(settingsCache, SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged())); - connect(settingsCache, SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged())); - - networkManager = new QNetworkAccessManager(this); - connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(picDownloadFinished(QNetworkReply *))); - - pictureLoaderThread = new QThread; - pictureLoaderThread->start(QThread::LowPriority); - moveToThread(pictureLoaderThread); -} - -PictureLoader::~PictureLoader() -{ - pictureLoaderThread->deleteLater(); -} - -void PictureLoader::processLoadQueue() -{ - if (loadQueueRunning) - return; - - loadQueueRunning = true; - forever { - mutex.lock(); - if (loadQueue.isEmpty()) { - mutex.unlock(); - loadQueueRunning = false; - return; - } - cardBeingLoaded = loadQueue.takeFirst(); - mutex.unlock(); - - QString setName = cardBeingLoaded.getSetName(); - QString correctedCardname = cardBeingLoaded.getCard()->getCorrectedName(); - qDebug() << "Trying to load picture (set: " << setName << " card: " << correctedCardname << ")"; - - //The list of paths to the folders in which to search for images - QList picsPaths = QList() << picsPath + "/CUSTOM/" + correctedCardname; - - if(!setName.isEmpty()) - { - picsPaths << picsPath + "/" + setName + "/" + correctedCardname - << picsPath + "/downloadedPics/" + setName + "/" + correctedCardname; - } - - QImage image; - QImageReader imgReader; - imgReader.setDecideFormatFromContent(true); - 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)) { - qDebug() << "Picture found on disk (set: " << setName << " card: " << correctedCardname << ")"; - imageLoaded(cardBeingLoaded.getCard(), image); - found = true; - break; - } - imgReader.setFileName(picsPaths.at(i) + ".full"); - if (imgReader.read(&image)) { - qDebug() << "Picture.full found on disk (set: " << setName << " card: " << correctedCardname << ")"; - imageLoaded(cardBeingLoaded.getCard(), image); - found = true; - } - } - - if (!found) { - if (picDownload) { - qDebug() << "Picture NOT found, trying to download (set: " << setName << " card: " << correctedCardname << ")"; - cardsToDownload.append(cardBeingLoaded); - cardBeingLoaded=0; - if (!downloadRunning) - startNextPicDownload(); - } else { - if (cardBeingLoaded.nextSet()) - { - qDebug() << "Picture NOT found and download disabled, moving to next set (newset: " << setName << " card: " << correctedCardname << ")"; - mutex.lock(); - loadQueue.prepend(cardBeingLoaded); - cardBeingLoaded=0; - mutex.unlock(); - } else { - qDebug() << "Picture NOT found, download disabled, no more sets to try: BAILING OUT (oldset: " << setName << " card: " << correctedCardname << ")"; - imageLoaded(cardBeingLoaded.getCard(), QImage()); - } - } - } - } -} - -QString PictureLoader::getPicUrl() -{ - if (!picDownload) return QString(""); - - CardInfo *card = cardBeingDownloaded.getCard(); - CardSet *set=cardBeingDownloaded.getCurrentSet(); - QString picUrl = QString(""); - - // if sets have been defined for the card, they can contain custom picUrls - if(set) - { - picUrl = card->getCustomPicURL(set->getShortName()); - if (!picUrl.isEmpty()) - return picUrl; - } - - // if a card has a muid, use the default url; if not, use the fallback - int muid = set ? card->getMuId(set->getShortName()) : 0; - picUrl = muid ? settingsCache->getPicUrl() : settingsCache->getPicUrlFallback(); - - picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName())); - picUrl.replace("!name_lower!", QUrl::toPercentEncoding(card->getCorrectedName().toLower())); - picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(muid))); - if (set) - { - picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName())); - picUrl.replace("!setcode_lower!", QUrl::toPercentEncoding(set->getShortName().toLower())); - picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName())); - picUrl.replace("!setname_lower!", QUrl::toPercentEncoding(set->getLongName().toLower())); - } - - if ( - picUrl.contains("!name!") || - picUrl.contains("!name_lower!") || - picUrl.contains("!setcode!") || - picUrl.contains("!setcode_lower!") || - picUrl.contains("!setname!") || - picUrl.contains("!setname_lower!") || - picUrl.contains("!cardid!") - ) - { - qDebug() << "Insufficient card data to download" << card->getName() << "Url:" << picUrl; - return QString(""); - } - - return picUrl; -} - -void PictureLoader::startNextPicDownload() -{ - if (cardsToDownload.isEmpty()) { - cardBeingDownloaded = 0; - downloadRunning = false; - return; - } - - downloadRunning = true; - - cardBeingDownloaded = cardsToDownload.takeFirst(); - - QString picUrl = getPicUrl(); - if (picUrl.isEmpty()) { - downloadRunning = false; - picDownloadFailed(); - } else { - QUrl url(picUrl); - - QNetworkRequest req(url); - qDebug() << "starting picture download:" << cardBeingDownloaded.getCard()->getName() << "Url:" << req.url(); - networkManager->get(req); - } -} - -void PictureLoader::picDownloadFailed() -{ - if (cardBeingDownloaded.nextSet()) - { - qDebug() << "Picture NOT found, download failed, moving to next set (newset: " << cardBeingDownloaded.getSetName() << " card: " << cardBeingDownloaded.getCard()->getCorrectedName() << ")"; - mutex.lock(); - loadQueue.prepend(cardBeingDownloaded); - mutex.unlock(); - emit startLoadQueue(); - } else { - qDebug() << "Picture NOT found, download failed, no more sets to try: BAILING OUT (oldset: " << cardBeingDownloaded.getSetName() << " card: " << cardBeingDownloaded.getCard()->getCorrectedName() << ")"; - cardBeingDownloaded = 0; - imageLoaded(cardBeingDownloaded.getCard(), QImage()); - } -} - -void PictureLoader::picDownloadFinished(QNetworkReply *reply) -{ - if (reply->error()) { - qDebug() << "Download failed:" << reply->errorString(); - } - - int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - if (statusCode == 301 || statusCode == 302) { - QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); - QNetworkRequest req(redirectUrl); - qDebug() << "following redirect:" << cardBeingDownloaded.getCard()->getName() << "Url:" << req.url(); - networkManager->get(req); - return; - } - - const QByteArray &picData = reply->peek(reply->size()); //peek is used to keep the data in the buffer for use by QImageReader - - // check if the image is blacklisted - QString md5sum = QCryptographicHash::hash(picData, QCryptographicHash::Md5).toHex(); - if(md5Blacklist.contains(md5sum)) - { - qDebug() << "Picture downloaded, but blacklisted (" << md5sum << "), will consider it as not found"; - picDownloadFailed(); - reply->deleteLater(); - startNextPicDownload(); - return; - } - - QImage testImage; - - QImageReader imgReader; - imgReader.setDecideFormatFromContent(true); - 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; - } - - QFile newPic(picsPath + "/downloadedPics/" + setName + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + extension); - if (!newPic.open(QIODevice::WriteOnly)) - return; - newPic.write(picData); - newPic.close(); - } - - imageLoaded(cardBeingDownloaded.getCard(), testImage); - } else { - picDownloadFailed(); - } - - reply->deleteLater(); - startNextPicDownload(); -} - -void PictureLoader::enqueueImageLoad(CardInfo *card) -{ - QMutexLocker locker(&mutex); - - // avoid queueing the same card more than once - if(card == 0 || card == cardBeingLoaded.getCard() || card == cardBeingDownloaded.getCard()) - return; - - foreach(PictureToLoad pic, loadQueue) - { - if(pic.getCard() == card) - return; - } - - loadQueue.append(PictureToLoad(card)); - emit startLoadQueue(); -} - -void PictureLoader::picDownloadChanged() -{ - QMutexLocker locker(&mutex); - picDownload = settingsCache->getPicDownload(); - - QPixmapCache::clear(); -} - -void PictureLoader::picsPathChanged() -{ - QMutexLocker locker(&mutex); - picsPath = settingsCache->getPicsPath(); - - QPixmapCache::clear(); -} - -void PictureLoader::getPixmap(QPixmap &pixmap, CardInfo *card, QSize size) -{ - QPixmap bigPixmap; - if(card) - { - // search for an exact size copy of the picure in cache - QString key = card->getPixmapCacheKey(); - QString sizekey = key + QLatin1Char('_') + QString::number(size.width()) + QString::number(size.height()); - if(QPixmapCache::find(sizekey, &pixmap)) - return; - - // load the image and create a copy of the correct size - if(QPixmapCache::find(key, &bigPixmap)) - { - pixmap = bigPixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); - QPixmapCache::insert(sizekey, pixmap); - return; - } - } - - // load a temporary back picture - QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + QString::number(size.height()); - if(!QPixmapCache::find(backCacheKey, &pixmap)) - { - pixmap = QPixmap("theme:cardback").scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); - QPixmapCache::insert(backCacheKey, pixmap); - } - - if(card) - { - // add the card to the load queue - getInstance().enqueueImageLoad(card); - } -} - - -void PictureLoader::imageLoaded(CardInfo *card, const QImage &image) -{ - if(image.isNull()) - return; - - if(card->getUpsideDownArt()) - { - QImage mirrorImage = image.mirrored(true, true); - QPixmapCache::insert(card->getPixmapCacheKey(), QPixmap::fromImage(mirrorImage)); - } else { - QPixmapCache::insert(card->getPixmapCacheKey(), QPixmap::fromImage(image)); - } - - card->emitPixmapUpdated(); -} - -void PictureLoader::clearPixmapCache(CardInfo *card) -{ - //qDebug() << "Deleting pixmap for" << name; - if(card) - QPixmapCache::remove(card->getPixmapCacheKey()); -} - -void PictureLoader::clearPixmapCache() -{ - QPixmapCache::clear(); -} - -void PictureLoader::cacheCardPixmaps(QList cards) -{ - QPixmap tmp; - // never cache more than 300 cards at once for a single deck - int max = qMin(cards.size(), 300); - for (int i = 0; i < max; ++i) - { - CardInfo * card = cards.at(i); - if(!card) - continue; - - QString key = card->getPixmapCacheKey(); - if(QPixmapCache::find(key, &tmp)) - continue; - - getInstance().enqueueImageLoad(card); - } -} diff --git a/cockatrice/src/pictureloader.h b/cockatrice/src/pictureloader.h deleted file mode 100644 index 7c21312d..00000000 --- a/cockatrice/src/pictureloader.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef PICTURELOADER_H -#define PICTURELOADER_H - -#include -#include -#include -#include - -class CardInfo; -class CardSet; -class QNetworkAccessManager; -class QNetworkReply; -class QThread; - -class PictureToLoad { -private: - class EnabledAndKeyCompareFunctor; - - CardInfo *card; - QList sortedSets; - int setIndex; - bool hq; -public: - PictureToLoad(CardInfo *_card = 0); - CardInfo *getCard() const { return card; } - CardSet *getCurrentSet() const; - QString getSetName() const; - bool nextSet(); -}; - -class PictureLoader : public QObject { -Q_OBJECT -public: - static PictureLoader& getInstance() - { - static PictureLoader instance; - return instance; - } -private: - PictureLoader(); - ~PictureLoader(); - // Don't implement - PictureLoader(PictureLoader const&); - void operator=(PictureLoader const&); - - static QStringList md5Blacklist; - - QThread *pictureLoaderThread; - QString picsPath; - QList loadQueue; - QMutex mutex; - QNetworkAccessManager *networkManager; - QList cardsToDownload; - PictureToLoad cardBeingLoaded; - PictureToLoad cardBeingDownloaded; - bool picDownload, downloadRunning, loadQueueRunning; - void startNextPicDownload(); - void imageLoaded(CardInfo *card, const QImage &image); - QString getPicUrl(); -public: - void enqueueImageLoad(CardInfo *card); - static void getPixmap(QPixmap &pixmap, CardInfo *card, QSize size); - static void clearPixmapCache(CardInfo *card); - static void clearPixmapCache(); - static void cacheCardPixmaps(QList cards); -private slots: - void picDownloadFinished(QNetworkReply *reply); - void picDownloadFailed(); - - void picDownloadChanged(); - void picsPathChanged(); -public slots: - void processLoadQueue(); -signals: - void startLoadQueue(); -}; - -#endif diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index 738515cd..d5eb062d 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -165,9 +165,12 @@ SettingsCache::SettingsCache() pixmapCacheSize = PIXMAPCACHE_SIZE_DEFAULT; picDownload = settings->value("personal/picturedownload", true).toBool(); + picDownloadHq = settings->value("personal/picturedownloadhq", true).toBool(); picUrl = settings->value("personal/picUrl", PIC_URL_DEFAULT).toString(); + picUrlHq = settings->value("personal/picUrlHq", PIC_URL_HQ_DEFAULT).toString(); picUrlFallback = settings->value("personal/picUrlFallback", PIC_URL_FALLBACK).toString(); + picUrlHqFallback = settings->value("personal/picUrlHqFallback", PIC_URL_HQ_FALLBACK).toString(); mainWindowGeometry = settings->value("interface/main_window_geometry").toByteArray(); notificationsEnabled = settings->value("interface/notificationsenabled", true).toBool(); @@ -322,18 +325,37 @@ void SettingsCache::setPicDownload(int _picDownload) emit picDownloadChanged(); } +void SettingsCache::setPicDownloadHq(int _picDownloadHq) +{ + picDownloadHq = _picDownloadHq; + settings->setValue("personal/picturedownloadhq", picDownloadHq); + emit picDownloadHqChanged(); +} + void SettingsCache::setPicUrl(const QString &_picUrl) { picUrl = _picUrl; settings->setValue("personal/picUrl", picUrl); } +void SettingsCache::setPicUrlHq(const QString &_picUrlHq) +{ + picUrlHq = _picUrlHq; + settings->setValue("personal/picUrlHq", picUrlHq); +} + void SettingsCache::setPicUrlFallback(const QString &_picUrlFallback) { picUrlFallback = _picUrlFallback; settings->setValue("personal/picUrlFallback", picUrlFallback); } +void SettingsCache::setPicUrlHqFallback(const QString &_picUrlHqFallback) +{ + picUrlHqFallback = _picUrlHqFallback; + settings->setValue("personal/picUrlHqFallback", picUrlHqFallback); +} + void SettingsCache::setNotificationsEnabled(int _notificationsEnabled) { notificationsEnabled = _notificationsEnabled; diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index 036f6966..722ccac1 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -14,6 +14,8 @@ // the falbacks are used for cards without a muid #define PIC_URL_DEFAULT "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!cardid!&type=card" #define PIC_URL_FALLBACK "http://gatherer.wizards.com/Handlers/Image.ashx?name=!name!&type=card" +#define PIC_URL_HQ_DEFAULT "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!cardid!&type=card" +#define PIC_URL_HQ_FALLBACK "http://gatherer.wizards.com/Handlers/Image.ashx?name=!name!&type=card" // size should be a multiple of 64 #define PIXMAPCACHE_SIZE_DEFAULT 2047 #define PIXMAPCACHE_SIZE_MIN 64 @@ -30,6 +32,7 @@ signals: void tokenDatabasePathChanged(); void themeChanged(); void picDownloadChanged(); + void picDownloadHqChanged(); void displayCardNamesChanged(); void horizontalHandChanged(); void handJustificationChanged(); @@ -57,6 +60,7 @@ private: QString deckPath, replaysPath, picsPath, cardDatabasePath, tokenDatabasePath, themeName; bool notifyAboutUpdates; bool picDownload; + bool picDownloadHq; bool notificationsEnabled; bool spectatorNotificationsEnabled; bool doubleClickToPlay; @@ -83,7 +87,9 @@ private: bool ignoreUnregisteredUsers; bool ignoreUnregisteredUserMessages; QString picUrl; + QString picUrlHq; QString picUrlFallback; + QString picUrlHqFallback; QString clientID; int pixmapCacheSize; bool scaleCards; @@ -121,6 +127,7 @@ public: QString getChatMentionColor() const { return chatMentionColor; } QString getChatHighlightColor() const { return chatHighlightColor; } bool getPicDownload() const { return picDownload; } + bool getPicDownloadHq() const { return picDownloadHq; } bool getNotificationsEnabled() const { return notificationsEnabled; } bool getSpectatorNotificationsEnabled() const { return spectatorNotificationsEnabled; } bool getNotifyAboutUpdates() const { return notifyAboutUpdates; } @@ -153,7 +160,9 @@ public: bool getIgnoreUnregisteredUsers() const { return ignoreUnregisteredUsers; } bool getIgnoreUnregisteredUserMessages() const { return ignoreUnregisteredUserMessages; } QString getPicUrl() const { return picUrl; } + QString getPicUrlHq() const { return picUrlHq; } QString getPicUrlFallback() const { return picUrlFallback; } + QString getPicUrlHqFallback() const { return picUrlHqFallback; } int getPixmapCacheSize() const { return pixmapCacheSize; } bool getScaleCards() const { return scaleCards; } bool getShowMessagePopup() const { return showMessagePopups; } @@ -195,6 +204,7 @@ public slots: void setChatMentionColor(const QString &_chatMentionColor); void setChatHighlightColor(const QString &_chatHighlightColor); void setPicDownload(int _picDownload); + void setPicDownloadHq(int _picDownloadHq); void setNotificationsEnabled(int _notificationsEnabled); void setSpectatorNotificationsEnabled(int _spectatorNotificationsEnabled); void setDoubleClickToPlay(int _doubleClickToPlay); @@ -221,7 +231,9 @@ public slots: void setIgnoreUnregisteredUsers(int _ignoreUnregisteredUsers); void setIgnoreUnregisteredUserMessages(int _ignoreUnregisteredUserMessages); void setPicUrl(const QString &_picUrl); + void setPicUrlHq(const QString &_picUrlHq); void setPicUrlFallback(const QString &_picUrlFallback); + void setPicUrlHqFallback(const QString &_picUrlHqFallback); void setPixmapCacheSize(const int _pixmapCacheSize); void setCardScaling(const int _scaleCards); void setShowMessagePopups(const int _showMessagePopups); diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index 1e27434b..d750e83b 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -26,7 +26,6 @@ #include "tab_deck_editor.h" #include "window_sets.h" #include "carddatabase.h" -#include "pictureloader.h" #include "carddatabasemodel.h" #include "decklistmodel.h" #include "cardinfowidget.h" @@ -1059,7 +1058,7 @@ void TabDeckEditor::setDeck(DeckLoader *_deck) deckView->expandAll(); setModified(false); - PictureLoader::cacheCardPixmaps(db->getCards(deckModel->getDeckList()->getCardList())); + db->cacheCardPixmaps(deckModel->getDeckList()->getCardList()); deckView->expandAll(); setModified(false); } diff --git a/cockatrice/src/tab_game.cpp b/cockatrice/src/tab_game.cpp index be3c8cf6..86b6308d 100644 --- a/cockatrice/src/tab_game.cpp +++ b/cockatrice/src/tab_game.cpp @@ -32,7 +32,6 @@ #include "main.h" #include "settingscache.h" #include "carddatabase.h" -#include "pictureloader.h" #include "replay_timeline_widget.h" #include "lineeditcompleter.h" @@ -243,7 +242,7 @@ void DeckViewContainer::deckSelectFinished(const Response &r) { const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext); DeckLoader newDeck(QString::fromStdString(resp.deck())); - PictureLoader::cacheCardPixmaps(db->getCards(newDeck.getCardList())); + db->cacheCardPixmaps(newDeck.getCardList()); setDeck(newDeck); } @@ -1045,7 +1044,7 @@ void TabGame::eventGameStateChanged(const Event_GameStateChanged &event, int /*e DeckViewContainer *deckViewContainer = deckViewContainers.value(playerId); if (playerInfo.has_deck_list()) { DeckLoader newDeck(QString::fromStdString(playerInfo.deck_list())); - PictureLoader::cacheCardPixmaps(db->getCards(newDeck.getCardList())); + db->cacheCardPixmaps(newDeck.getCardList()); deckViewContainer->setDeck(newDeck); player->setDeck(newDeck); } diff --git a/cockatrice/src/window_sets.cpp b/cockatrice/src/window_sets.cpp index 5e0a49e2..b647e0a9 100644 --- a/cockatrice/src/window_sets.cpp +++ b/cockatrice/src/window_sets.cpp @@ -1,8 +1,6 @@ #include "window_sets.h" #include "setsmodel.h" -#include "pictureloader.h" #include "main.h" - #include #include #include @@ -125,7 +123,7 @@ WndSets::~WndSets() void WndSets::actSave() { model->save(db); - PictureLoader::clearPixmapCache(); + db->clearPixmapCache(); QMessageBox::information(this, tr("Success"), tr("The sets database has been saved successfully.")); close(); } diff --git a/doc/cards.xsd b/doc/cards.xsd index e5b33e35..d10032a7 100644 --- a/doc/cards.xsd +++ b/doc/cards.xsd @@ -29,6 +29,7 @@ + diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index c7c7cb28..05c8a2e4 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -12,7 +12,6 @@ SET(oracle_SOURCES src/oraclewizard.cpp src/oracleimporter.cpp ../cockatrice/src/carddatabase.cpp - ../cockatrice/src/pictureloader.cpp ../cockatrice/src/settingscache.cpp ../cockatrice/src/shortcutssettings.cpp ../cockatrice/src/settings/carddatabasesettings.cpp diff --git a/oracle/src/oracleimporter.h b/oracle/src/oracleimporter.h index 17092ea5..888eabaf 100644 --- a/oracle/src/oracleimporter.h +++ b/oracle/src/oracleimporter.h @@ -2,7 +2,6 @@ #define ORACLEIMPORTER_H #include -#include #include