Warning, file /plasma/kdeplasma-addons/applets/comic/engine/comicproviderwrapper.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  *   SPDX-FileCopyrightText: 2008 Petri Damstén <damu@iki.fi>
0003  *   SPDX-FileCopyrightText: 2010 Matthias Fuchs <mat69@gmx.net>
0004  *
0005  *   SPDX-License-Identifier: LGPL-2.0-only
0006  */
0007 
0008 #include "comicproviderwrapper.h"
0009 #include "comic_debug.h"
0010 #include "comicproviderkross.h"
0011 
0012 #include <KPackage/Package>
0013 #include <KPackage/PackageLoader>
0014 #include <QFile>
0015 #include <QFileInfo>
0016 #include <QJSEngine>
0017 #include <QJSValueIterator>
0018 #include <QJSValueList>
0019 #include <QPainter>
0020 #include <QStandardPaths>
0021 #include <QTextCodec>
0022 #include <QTimer>
0023 #include <QUrl>
0024 
0025 QStringList ComicProviderWrapper::mExtensions;
0026 
0027 ImageWrapper::ImageWrapper(QObject *parent, const QByteArray &data)
0028     : QObject(parent)
0029     , mImage(QImage::fromData(data))
0030     , mRawData(data)
0031 {
0032     resetImageReader();
0033 }
0034 
0035 QImage ImageWrapper::image() const
0036 {
0037     return mImage;
0038 }
0039 
0040 void ImageWrapper::setImage(const QImage &image)
0041 {
0042     mImage = image;
0043     mRawData.clear();
0044 
0045     resetImageReader();
0046 }
0047 
0048 QByteArray ImageWrapper::rawData() const
0049 {
0050     if (mRawData.isNull()) {
0051         QBuffer buffer(&mRawData);
0052         mImage.save(&buffer);
0053     }
0054 
0055     return mRawData;
0056 }
0057 
0058 void ImageWrapper::setRawData(const QByteArray &rawData)
0059 {
0060     mRawData = rawData;
0061     mImage = QImage::fromData(mRawData);
0062 
0063     resetImageReader();
0064 }
0065 
0066 void ImageWrapper::resetImageReader()
0067 {
0068     if (mBuffer.isOpen()) {
0069         mBuffer.close();
0070     }
0071     rawData(); // to update the rawData if needed
0072     mBuffer.setBuffer(&mRawData);
0073     mBuffer.open(QIODevice::ReadOnly);
0074     mImageReader.setDevice(&mBuffer);
0075 }
0076 
0077 int ImageWrapper::imageCount() const
0078 {
0079     return mImageReader.imageCount();
0080 }
0081 
0082 QImage ImageWrapper::read()
0083 {
0084     return mImageReader.read();
0085 }
0086 
0087 DateWrapper::DateWrapper(const QDate &date)
0088     : mDate(date)
0089 {
0090 }
0091 
0092 QDate DateWrapper::date() const
0093 {
0094     return mDate;
0095 }
0096 
0097 void DateWrapper::setDate(const QDate &date)
0098 {
0099     mDate = date;
0100 }
0101 
0102 QDate DateWrapper::fromVariant(const QVariant &variant)
0103 {
0104     if (variant.type() == QVariant::Date || variant.type() == QVariant::DateTime) {
0105         return variant.toDate();
0106     } else if (variant.type() == QVariant::String) {
0107         return QDate::fromString(variant.toString(), Qt::ISODate);
0108     } else {
0109         if (variant.canConvert<DateWrapper>()) {
0110             return variant.value<DateWrapper>().date();
0111         }
0112     }
0113     return QDate();
0114 }
0115 
0116 DateWrapper DateWrapper::addDays(int ndays)
0117 {
0118     return DateWrapper(mDate.addDays(ndays));
0119 }
0120 
0121 DateWrapper DateWrapper::addMonths(int nmonths)
0122 {
0123     return DateWrapper(mDate.addMonths(nmonths));
0124 }
0125 
0126 DateWrapper DateWrapper::addYears(int nyears)
0127 {
0128     return DateWrapper(mDate.addYears(nyears));
0129 }
0130 
0131 int DateWrapper::day() const
0132 {
0133     return mDate.day();
0134 }
0135 
0136 int DateWrapper::dayOfWeek() const
0137 {
0138     return mDate.dayOfWeek();
0139 }
0140 
0141 int DateWrapper::dayOfYear() const
0142 {
0143     return mDate.dayOfYear();
0144 }
0145 
0146 int DateWrapper::daysInMonth() const
0147 {
0148     return mDate.daysInMonth();
0149 }
0150 
0151 int DateWrapper::daysInYear() const
0152 {
0153     return mDate.daysInYear();
0154 }
0155 
0156 int DateWrapper::daysTo(const QVariant d) const
0157 {
0158     return mDate.daysTo(fromVariant(d));
0159 }
0160 
0161 bool DateWrapper::isNull() const
0162 {
0163     return mDate.isNull();
0164 }
0165 
0166 bool DateWrapper::isValid() const
0167 {
0168     return mDate.isValid();
0169 }
0170 
0171 int DateWrapper::month() const
0172 {
0173     return mDate.month();
0174 }
0175 
0176 bool DateWrapper::setDate(int year, int month, int day)
0177 {
0178     return mDate.setDate(year, month, day);
0179 }
0180 
0181 int DateWrapper::toJulianDay() const
0182 {
0183     return mDate.toJulianDay();
0184 }
0185 
0186 QString DateWrapper::toString(const QString &format) const
0187 {
0188     return mDate.toString(format);
0189 }
0190 
0191 QString DateWrapper::toString(int format) const
0192 {
0193     return mDate.toString((Qt::DateFormat)format);
0194 }
0195 
0196 int DateWrapper::weekNumber() const
0197 {
0198     return mDate.weekNumber();
0199 }
0200 
0201 int DateWrapper::year() const
0202 {
0203     return mDate.year();
0204 }
0205 
0206 StaticDateWrapper::StaticDateWrapper(QObject *parent)
0207     : QObject(parent)
0208 {
0209 }
0210 
0211 DateWrapper StaticDateWrapper::currentDate()
0212 {
0213     return DateWrapper(QDate::currentDate());
0214 }
0215 
0216 DateWrapper StaticDateWrapper::fromJulianDay(int jd)
0217 {
0218     return DateWrapper(QDate::fromJulianDay(jd));
0219 }
0220 
0221 DateWrapper StaticDateWrapper::fromString(const QString &string, int format)
0222 {
0223     return DateWrapper(QDate::fromString(string, (Qt::DateFormat)format));
0224 }
0225 
0226 DateWrapper StaticDateWrapper::fromString(const QString &string, const QString &format)
0227 {
0228     return DateWrapper(QDate::fromString(string, format));
0229 }
0230 
0231 bool StaticDateWrapper::isLeapYear(int year)
0232 {
0233     return QDate::isLeapYear(year);
0234 }
0235 
0236 bool StaticDateWrapper::isValid(int year, int month, int day)
0237 {
0238     return QDate::isValid(year, month, day);
0239 }
0240 
0241 QString StaticDateWrapper::longDayName(int weekday)
0242 {
0243     return QLocale::system().dayName(weekday, QLocale::LongFormat);
0244 }
0245 
0246 QString StaticDateWrapper::longMonthName(int month)
0247 {
0248     return QLocale::system().monthName(month, QLocale::LongFormat);
0249 }
0250 
0251 QString StaticDateWrapper::shortDayName(int weekday)
0252 {
0253     return QLocale::system().dayName(weekday, QLocale::ShortFormat);
0254 }
0255 
0256 QString StaticDateWrapper::shortMonthName(int month)
0257 {
0258     return QLocale::system().monthName(month, QLocale::ShortFormat);
0259 }
0260 
0261 ComicProviderWrapper::ComicProviderWrapper(ComicProviderKross *parent)
0262     : QObject(parent)
0263     , mProvider(parent)
0264     , mKrossImage(nullptr)
0265     , mPackage(nullptr)
0266     , mRequests(0)
0267     , mIdentifierSpecified(false)
0268     , mIsLeftToRight(true)
0269     , mIsTopToBottom(true)
0270 {
0271     QTimer::singleShot(0, this, &ComicProviderWrapper::init);
0272 }
0273 
0274 ComicProviderWrapper::~ComicProviderWrapper()
0275 {
0276     delete mPackage;
0277 }
0278 
0279 void ComicProviderWrapper::init()
0280 {
0281     const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
0282                                                 QLatin1String("plasma/comics/") + mProvider->pluginName() + QLatin1Char('/'),
0283                                                 QStandardPaths::LocateDirectory);
0284     qCDebug(PLASMA_COMIC) << "ComicProviderWrapper::init() package is" << mProvider->pluginName() << " at " << path;
0285 
0286     if (!path.isEmpty()) {
0287         mPackage = new KPackage::Package(KPackage::PackageLoader::self()->loadPackageStructure(QStringLiteral("Plasma/Comic")));
0288         mPackage->setPath(path);
0289 
0290         if (mPackage->isValid()) {
0291             // QString mainscript = mPackage->filePath("mainscript")
0292             // doesn't work because the mainscript file can be a different Kross script type ending with main.es or main.kjs etc.
0293             // Maybe this should be added to Plasma::Package or maybe directly change the packagestructure mainscript definition as "main.es" and avoid all
0294             // this. https://techbase.kde.org/Development/Tutorials/Plasma4/ComicPlugin#Package_Structure has main.es defined as mainscript. Also
0295             // Package::isValid() fails because the mainscript search fails to find the "main" file from mainscript.
0296 
0297             QString mainscript = mPackage->filePath("scripts") + QLatin1String("/main");
0298             const QStringList extensions{QStringLiteral(".es"), QStringLiteral(".js")};
0299             QFileInfo info(mainscript);
0300             for (int i = 0; i < extensions.count() && !info.exists(); ++i) {
0301                 info.setFile(mainscript + extensions.value(i));
0302                 qCDebug(PLASMA_COMIC) << "ComicProviderWrapper::init() mainscript found as" << info.filePath();
0303             }
0304 
0305             if (info.exists()) {
0306                 m_engine = new QJSEngine(this);
0307                 QFile f(info.absoluteFilePath());
0308                 if (f.open(QFile::ReadOnly)) {
0309                     m_engine->globalObject().setProperty("Comic", m_engine->newQMetaObject(&ComicProviderWrapper::staticMetaObject));
0310                     auto obj = m_engine->newQObject(this);
0311 
0312                     // If we set the comic in the global object we can not access the staticMetaObject
0313                     // consequently the values have to be written manually
0314                     obj.setProperty("Page", ComicProvider::Page);
0315                     obj.setProperty("Image", ComicProvider::Image);
0316                     obj.setProperty("User", ComicProvider::User);
0317                     obj.setProperty("Left", ComicProviderWrapper::Left);
0318                     obj.setProperty("Top", ComicProviderWrapper::Top);
0319                     obj.setProperty("Right", ComicProviderWrapper::Right);
0320                     obj.setProperty("Bottom", ComicProviderWrapper::Bottom);
0321                     obj.setProperty("DateIdentifier", (int)IdentifierType::DateIdentifier);
0322                     obj.setProperty("NumberIdentifier", (int)IdentifierType::NumberIdentifier);
0323                     obj.setProperty("StringIdentifier", (int)IdentifierType::StringIdentifier);
0324 
0325                     m_engine->globalObject().setProperty("comic", obj);
0326                     m_engine->globalObject().setProperty("date", m_engine->newQObject(new StaticDateWrapper(this)));
0327                     m_engine->evaluate("var print = comic.print");
0328                     mIdentifierSpecified = !mProvider->isCurrent();
0329                     m_engine->evaluate(f.readAll(), info.absoluteFilePath());
0330                     QJSValueIterator it(m_engine->globalObject());
0331                     while (it.hasNext()) {
0332                         it.next();
0333                         if (it.value().isCallable()) {
0334                             mFunctions << it.name();
0335                         }
0336                     }
0337                     setIdentifierToDefault();
0338                     callFunction(QStringLiteral("init"));
0339                 }
0340             }
0341         }
0342     }
0343 }
0344 
0345 IdentifierType ComicProviderWrapper::identifierType() const
0346 {
0347     IdentifierType result = IdentifierType::StringIdentifier;
0348     const QString type = mProvider->description().value(QLatin1String("X-KDE-PlasmaComicProvider-SuffixType"));
0349     if (type == QLatin1String("Date")) {
0350         result = IdentifierType::DateIdentifier;
0351     } else if (type == QLatin1String("Number")) {
0352         result = IdentifierType::NumberIdentifier;
0353     } else if (type == QLatin1String("String")) {
0354         result = IdentifierType::StringIdentifier;
0355     }
0356     return result;
0357 }
0358 
0359 QImage ComicProviderWrapper::comicImage()
0360 {
0361     ImageWrapper *img = qobject_cast<ImageWrapper *>(callFunction(QLatin1String("image")).value<QObject *>());
0362     if (functionCalled() && img) {
0363         return img->image();
0364     }
0365     if (mKrossImage) {
0366         return mKrossImage->image();
0367     }
0368     return QImage();
0369 }
0370 
0371 QJSValue ComicProviderWrapper::identifierToScript(const QVariant &identifier)
0372 {
0373     if (identifierType() == IdentifierType::DateIdentifier && identifier.type() != QVariant::Bool) {
0374         return m_engine->toScriptValue(DateWrapper(identifier.toDate()));
0375     }
0376     return m_engine->toScriptValue(identifier);
0377 }
0378 
0379 QVariant ComicProviderWrapper::identifierFromScript(const QJSValue &identifier) const
0380 {
0381     if (identifier.toVariant().canConvert<DateWrapper>()) {
0382         return identifier.toVariant().value<DateWrapper>().date();
0383     }
0384     return identifier.toVariant();
0385 }
0386 
0387 void ComicProviderWrapper::checkIdentifier(QVariant *identifier)
0388 {
0389     switch (identifierType()) {
0390     case IdentifierType::DateIdentifier:
0391         if (!mLastIdentifier.isNull() && !identifier->isNull() && (!mIdentifierSpecified || identifier->toDate() > mLastIdentifier.toDate())) {
0392             *identifier = mLastIdentifier;
0393         }
0394         if (!mFirstIdentifier.isNull() && !identifier->isNull() && identifier->toDate() < mFirstIdentifier.toDate()) {
0395             *identifier = mFirstIdentifier;
0396         }
0397         break;
0398     case IdentifierType::NumberIdentifier:
0399         if (!mLastIdentifier.isNull() && !identifier->isNull() && (!mIdentifierSpecified || identifier->toInt() > mLastIdentifier.toInt())) {
0400             *identifier = mLastIdentifier;
0401         }
0402         if (!mFirstIdentifier.isNull() && !identifier->isNull() && identifier->toInt() < mFirstIdentifier.toInt()) {
0403             *identifier = mFirstIdentifier;
0404         }
0405         break;
0406     case IdentifierType::StringIdentifier:
0407         if (!mLastIdentifier.isNull() && !mLastIdentifier.toString().isEmpty() && !mIdentifierSpecified) {
0408             *identifier = mLastIdentifier;
0409         }
0410         break;
0411     }
0412 }
0413 
0414 void ComicProviderWrapper::setIdentifierToDefault()
0415 {
0416     switch (identifierType()) {
0417     case IdentifierType::DateIdentifier:
0418         mIdentifier = mProvider->requestedDate();
0419         mLastIdentifier = QDate::currentDate();
0420         break;
0421     case IdentifierType::NumberIdentifier:
0422         mIdentifier = mProvider->requestedNumber();
0423         mFirstIdentifier = 1;
0424         break;
0425     case IdentifierType::StringIdentifier:
0426         mIdentifier = mProvider->requestedString();
0427         break;
0428     }
0429 }
0430 
0431 bool ComicProviderWrapper::identifierSpecified() const
0432 {
0433     return mIdentifierSpecified;
0434 }
0435 
0436 bool ComicProviderWrapper::isLeftToRight() const
0437 {
0438     return mIsLeftToRight;
0439 }
0440 
0441 void ComicProviderWrapper::setLeftToRight(bool ltr)
0442 {
0443     mIsLeftToRight = ltr;
0444 }
0445 
0446 bool ComicProviderWrapper::isTopToBottom() const
0447 {
0448     return mIsTopToBottom;
0449 }
0450 
0451 void ComicProviderWrapper::setTopToBottom(bool ttb)
0452 {
0453     mIsTopToBottom = ttb;
0454 }
0455 
0456 QString ComicProviderWrapper::textCodec() const
0457 {
0458     return QString::fromLatin1(mTextCodec);
0459 }
0460 
0461 void ComicProviderWrapper::setTextCodec(const QString &textCodec)
0462 {
0463     mTextCodec = textCodec.toLatin1();
0464 }
0465 
0466 QString ComicProviderWrapper::comicAuthor() const
0467 {
0468     return mProvider->comicAuthor();
0469 }
0470 
0471 void ComicProviderWrapper::setComicAuthor(const QString &author)
0472 {
0473     mProvider->setComicAuthor(author);
0474 }
0475 
0476 QString ComicProviderWrapper::websiteUrl() const
0477 {
0478     return mWebsiteUrl;
0479 }
0480 
0481 void ComicProviderWrapper::setWebsiteUrl(const QString &websiteUrl)
0482 {
0483     mWebsiteUrl = websiteUrl;
0484 }
0485 
0486 QString ComicProviderWrapper::shopUrl() const
0487 {
0488     return mShopUrl;
0489 }
0490 
0491 void ComicProviderWrapper::setShopUrl(const QString &shopUrl)
0492 {
0493     mShopUrl = shopUrl;
0494 }
0495 
0496 QString ComicProviderWrapper::title() const
0497 {
0498     return mTitle;
0499 }
0500 
0501 void ComicProviderWrapper::setTitle(const QString &title)
0502 {
0503     mTitle = title;
0504 }
0505 
0506 QString ComicProviderWrapper::additionalText() const
0507 {
0508     return mAdditionalText;
0509 }
0510 
0511 void ComicProviderWrapper::setAdditionalText(const QString &additionalText)
0512 {
0513     mAdditionalText = additionalText;
0514 }
0515 
0516 QJSValue ComicProviderWrapper::identifier()
0517 {
0518     return identifierToScript(mIdentifier);
0519 }
0520 
0521 void ComicProviderWrapper::setIdentifier(const QJSValue &identifier)
0522 {
0523     mIdentifier = identifierFromScript(identifier);
0524     checkIdentifier(&mIdentifier);
0525 }
0526 
0527 QJSValue ComicProviderWrapper::nextIdentifier()
0528 {
0529     return identifierToScript(mNextIdentifier);
0530 }
0531 
0532 void ComicProviderWrapper::setNextIdentifier(const QJSValue &nextIdentifier)
0533 {
0534     mNextIdentifier = identifierFromScript(nextIdentifier);
0535     if (mNextIdentifier == mIdentifier) {
0536         mNextIdentifier.clear();
0537         qCWarning(PLASMA_COMIC) << "Next identifier is the same as the current one, clearing next identifier.";
0538     }
0539 }
0540 
0541 QJSValue ComicProviderWrapper::previousIdentifier()
0542 {
0543     return identifierToScript(mPreviousIdentifier);
0544 }
0545 
0546 void ComicProviderWrapper::setPreviousIdentifier(const QJSValue &previousIdentifier)
0547 {
0548     mPreviousIdentifier = identifierFromScript(previousIdentifier);
0549     if (mPreviousIdentifier == mIdentifier) {
0550         mPreviousIdentifier.clear();
0551         qCWarning(PLASMA_COMIC) << "Previous identifier is the same as the current one, clearing previous identifier.";
0552     }
0553 }
0554 
0555 QJSValue ComicProviderWrapper::firstIdentifier()
0556 {
0557     return identifierToScript(mFirstIdentifier);
0558 }
0559 
0560 void ComicProviderWrapper::setFirstIdentifier(const QJSValue &firstIdentifier)
0561 {
0562     switch (identifierType()) {
0563     case IdentifierType::DateIdentifier:
0564         mProvider->setFirstStripDate(DateWrapper::fromVariant(QVariant::fromValue(firstIdentifier.toQObject())));
0565         break;
0566     case IdentifierType::NumberIdentifier:
0567         mProvider->setFirstStripNumber(firstIdentifier.toInt());
0568         break;
0569     case IdentifierType::StringIdentifier:
0570         break;
0571     }
0572     mFirstIdentifier = identifierFromScript(firstIdentifier);
0573     checkIdentifier(&mIdentifier);
0574 }
0575 
0576 QJSValue ComicProviderWrapper::lastIdentifier()
0577 {
0578     return identifierToScript(mLastIdentifier);
0579 }
0580 
0581 void ComicProviderWrapper::setLastIdentifier(const QJSValue &lastIdentifier)
0582 {
0583     mLastIdentifier = identifierFromScript(lastIdentifier);
0584     checkIdentifier(&mIdentifier);
0585 }
0586 
0587 QVariant ComicProviderWrapper::identifierVariant() const
0588 {
0589     return mIdentifier;
0590 }
0591 
0592 QVariant ComicProviderWrapper::firstIdentifierVariant() const
0593 {
0594     return mFirstIdentifier;
0595 }
0596 
0597 QVariant ComicProviderWrapper::lastIdentifierVariant() const
0598 {
0599     return mLastIdentifier;
0600 }
0601 
0602 QVariant ComicProviderWrapper::nextIdentifierVariant() const
0603 {
0604     // either handle both previousIdentifier and nextIdentifier or handle none
0605     if (mPreviousIdentifier.isNull() && mNextIdentifier.isNull()) {
0606         switch (identifierType()) {
0607         case IdentifierType::DateIdentifier:
0608             if ((mLastIdentifier.isNull() && mIdentifier.toDate() < QDate::currentDate())
0609                 || (!mLastIdentifier.isNull() && mIdentifier.toDate() < mLastIdentifier.toDate())) {
0610                 return mIdentifier.toDate().addDays(1);
0611             } else {
0612                 return false;
0613             }
0614         case IdentifierType::NumberIdentifier:
0615             if (mLastIdentifier.isNull() || mIdentifier.toInt() < mLastIdentifier.toInt()) {
0616                 return mIdentifier.toInt() + 1;
0617             } else {
0618                 return false;
0619             }
0620         case IdentifierType::StringIdentifier:
0621             break;
0622         }
0623         // check if the nextIdentifier is correct
0624     } else if (!mNextIdentifier.isNull()) {
0625         // no nextIdentifier if mIdentifier == mLastIdentifier or if no identifier has been specified
0626         switch (identifierType()) {
0627         case IdentifierType::DateIdentifier:
0628             if ((!mLastIdentifier.isNull() && (mIdentifier.toDate() == mLastIdentifier.toDate())) || !mIdentifierSpecified) {
0629                 return false;
0630             }
0631             break;
0632         case IdentifierType::NumberIdentifier:
0633             if ((!mLastIdentifier.isNull() && (mIdentifier.toInt() == mLastIdentifier.toInt())) || !mIdentifierSpecified) {
0634                 return false;
0635             }
0636             break;
0637         case IdentifierType::StringIdentifier:
0638             if (!mIdentifierSpecified) {
0639                 return false;
0640             }
0641             break;
0642         }
0643     }
0644     return mNextIdentifier;
0645 }
0646 
0647 QVariant ComicProviderWrapper::previousIdentifierVariant() const
0648 {
0649     // either handle both previousIdentifier and nextIdentifier or handle none
0650     if (mPreviousIdentifier.isNull() && mNextIdentifier.isNull()) {
0651         switch (identifierType()) {
0652         case IdentifierType::DateIdentifier:
0653             if (mFirstIdentifier.isNull() || mIdentifier.toDate() > mFirstIdentifier.toDate()) {
0654                 return mIdentifier.toDate().addDays(-1);
0655             } else {
0656                 return false;
0657             }
0658         case IdentifierType::NumberIdentifier:
0659             if ((mFirstIdentifier.isNull() && mIdentifier.toInt() > 1) || (!mFirstIdentifier.isNull() && mIdentifier.toInt() > mFirstIdentifier.toInt())) {
0660                 return mIdentifier.toInt() - 1;
0661             } else {
0662                 return false;
0663             }
0664         case IdentifierType::StringIdentifier:
0665             break;
0666         }
0667     } else if (!mPreviousIdentifier.isNull()) {
0668         // no previousIdentifier if mIdentifier == mFirstIdentifier
0669         switch (identifierType()) {
0670         case IdentifierType::DateIdentifier:
0671             if (!mFirstIdentifier.isNull() && (mIdentifier.toDate() == mFirstIdentifier.toDate())) {
0672                 return false;
0673             }
0674             break;
0675         case IdentifierType::NumberIdentifier:
0676             if (!mFirstIdentifier.isNull() && (mIdentifier.toInt() == mFirstIdentifier.toInt())) {
0677                 return false;
0678             }
0679             break;
0680         case IdentifierType::StringIdentifier:
0681             break;
0682         }
0683     }
0684     return mPreviousIdentifier;
0685 }
0686 
0687 void ComicProviderWrapper::pageRetrieved(int id, const QByteArray &data)
0688 {
0689     --mRequests;
0690     if (id == ComicProvider::Image) {
0691         mKrossImage = new ImageWrapper(this, data);
0692         callFunction(QLatin1String("pageRetrieved"), {id, m_engine->newQObject(mKrossImage)});
0693         if (mRequests < 1) { // Don't finish if we still have pageRequests
0694             finished();
0695         }
0696     } else {
0697         QTextCodec *codec = nullptr;
0698         if (!mTextCodec.isEmpty()) {
0699             codec = QTextCodec::codecForName(mTextCodec);
0700         }
0701         if (!codec) {
0702             codec = QTextCodec::codecForHtml(data);
0703         }
0704         QString html = codec->toUnicode(data);
0705 
0706         callFunction(QLatin1String("pageRetrieved"), {id, html});
0707     }
0708 }
0709 
0710 void ComicProviderWrapper::pageError(int id, const QString &message)
0711 {
0712     --mRequests;
0713     callFunction(QLatin1String("pageError"), {id, message});
0714     if (!functionCalled()) {
0715         Q_EMIT mProvider->error(mProvider);
0716     }
0717 }
0718 
0719 void ComicProviderWrapper::redirected(int id, const QUrl &newUrl)
0720 {
0721     --mRequests;
0722     callFunction(QLatin1String("redirected"), {id, newUrl.toString()});
0723     if (mRequests < 1) { // Don't finish while there are still requests
0724         finished();
0725     }
0726 }
0727 
0728 void ComicProviderWrapper::finished() const
0729 {
0730     qCDebug(PLASMA_COMIC) << QString::fromLatin1("Author").leftJustified(22, QLatin1Char('.')) << comicAuthor();
0731     qCDebug(PLASMA_COMIC) << QString::fromLatin1("Website URL").leftJustified(22, QLatin1Char('.')) << mWebsiteUrl;
0732     qCDebug(PLASMA_COMIC) << QString::fromLatin1("Shop URL").leftJustified(22, QLatin1Char('.')) << mShopUrl;
0733     qCDebug(PLASMA_COMIC) << QString::fromLatin1("Title").leftJustified(22, QLatin1Char('.')) << mTitle;
0734     qCDebug(PLASMA_COMIC) << QString::fromLatin1("Additional Text").leftJustified(22, QLatin1Char('.')) << mAdditionalText;
0735     qCDebug(PLASMA_COMIC) << QString::fromLatin1("Identifier").leftJustified(22, QLatin1Char('.')) << mIdentifier;
0736     qCDebug(PLASMA_COMIC) << QString::fromLatin1("First Identifier").leftJustified(22, QLatin1Char('.')) << mFirstIdentifier;
0737     qCDebug(PLASMA_COMIC) << QString::fromLatin1("Last Identifier").leftJustified(22, QLatin1Char('.')) << mLastIdentifier;
0738     qCDebug(PLASMA_COMIC) << QString::fromLatin1("Next Identifier").leftJustified(22, QLatin1Char('.')) << mNextIdentifier;
0739     qCDebug(PLASMA_COMIC) << QString::fromLatin1("Previous Identifier").leftJustified(22, QLatin1Char('.')) << mPreviousIdentifier;
0740     Q_EMIT mProvider->finished(mProvider);
0741 }
0742 
0743 void ComicProviderWrapper::error() const
0744 {
0745     Q_EMIT mProvider->error(mProvider);
0746 }
0747 
0748 void ComicProviderWrapper::requestPage(const QString &url, int id, const QVariantMap &infos)
0749 {
0750     QMap<QString, QString> map;
0751 
0752     for (auto it = infos.begin(), end = infos.end(); it != end; ++it) {
0753         map[it.key()] = it.value().toString();
0754     }
0755     mProvider->requestPage(QUrl(url), id, map);
0756     ++mRequests;
0757 }
0758 
0759 void ComicProviderWrapper::requestRedirectedUrl(const QString &url, int id, const QVariantMap &infos)
0760 {
0761     QMap<QString, QString> map;
0762 
0763     for (auto it = infos.begin(), end = infos.end(); it != end; ++it) {
0764         map[it.key()] = it.value().toString();
0765     }
0766     mProvider->requestRedirectedUrl(QUrl(url), id, map);
0767     ++mRequests;
0768 }
0769 
0770 bool ComicProviderWrapper::functionCalled() const
0771 {
0772     return mFuncFound;
0773 }
0774 
0775 QVariant ComicProviderWrapper::callFunction(const QString &name, const QJSValueList &args)
0776 {
0777     if (m_engine) {
0778         mFuncFound = mFunctions.contains(name);
0779         if (mFuncFound) {
0780             auto val = m_engine->globalObject().property(name).call(args);
0781             if (val.isError()) {
0782                 qCWarning(PLASMA_COMIC) << "Error when calling function" << name << "with arguments" << QVariant::fromValue(args) << val.toString();
0783                 return QVariant();
0784             } else {
0785                 return val.toVariant();
0786             }
0787         }
0788     }
0789     return QVariant();
0790 }
0791 
0792 void ComicProviderWrapper::combine(const QVariant &image, PositionType position)
0793 {
0794     if (!mKrossImage) {
0795         return;
0796     }
0797 
0798     QImage header;
0799     if (image.type() == QVariant::String) {
0800         const QString path(mPackage->filePath("images", image.toString()));
0801         if (QFile::exists(path)) {
0802             header = QImage(path);
0803         } else {
0804             return;
0805         }
0806     } else {
0807         ImageWrapper *img = qobject_cast<ImageWrapper *>(image.value<QObject *>());
0808         if (img) {
0809             header = img->image();
0810         } else {
0811             return;
0812         }
0813     }
0814     const QImage comic = mKrossImage->image();
0815     int height = 0;
0816     int width = 0;
0817 
0818     switch (position) {
0819     case Top:
0820     case Bottom:
0821         height = header.height() + comic.height();
0822         width = (header.width() >= comic.width()) ? header.width() : comic.width();
0823         break;
0824     case Left:
0825     case Right:
0826         height = (header.height() >= comic.height()) ? header.height() : comic.height();
0827         width = header.width() + comic.width();
0828         break;
0829     }
0830 
0831     QImage img = QImage(QSize(width, height), QImage::Format_RGB32);
0832     img.fill(header.pixel(QPoint(0, 0)));
0833 
0834     QPainter painter(&img);
0835 
0836     // center and draw the Images
0837     QPoint headerPos;
0838     QPoint comicPos;
0839 
0840     switch (position) {
0841     case Top:
0842         headerPos = QPoint(((width - header.width()) / 2), 0);
0843         comicPos = QPoint(((width - comic.width()) / 2), header.height());
0844         break;
0845     case Bottom:
0846         headerPos = QPoint(((width - header.width()) / 2), comic.height());
0847         comicPos = QPoint(((width - comic.width()) / 2), 0);
0848         break;
0849     case Left:
0850         headerPos = QPoint(0, ((height - header.height()) / 2));
0851         comicPos = QPoint(header.width(), ((height - comic.height()) / 2));
0852         break;
0853     case Right:
0854         headerPos = QPoint(comic.width(), ((height - header.height()) / 2));
0855         comicPos = QPoint(0, ((height - comic.height()) / 2));
0856         break;
0857     }
0858     painter.drawImage(headerPos, header);
0859     painter.drawImage(comicPos, comic);
0860     mKrossImage->setImage(img);
0861 }
0862 
0863 QObject *ComicProviderWrapper::image()
0864 {
0865     return qobject_cast<QObject *>(mKrossImage);
0866 }