2015-06-28 14:22:35 +03:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2015 Jolla Ltd.
|
|
|
|
* Contact: Slava Monich <slava.monich@jolla.com>
|
|
|
|
*
|
|
|
|
* You may use this file under the terms of the BSD license as follows:
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
*
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in
|
|
|
|
* the documentation and/or other materials provided with the
|
|
|
|
* distribution.
|
|
|
|
* * Neither the name of Nemo Mobile nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this
|
|
|
|
* software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "BooksBook.h"
|
|
|
|
#include "BooksDefs.h"
|
|
|
|
#include "BooksTask.h"
|
|
|
|
#include "BooksTextView.h"
|
|
|
|
#include "BooksTextStyle.h"
|
|
|
|
#include "BooksPaintContext.h"
|
|
|
|
|
|
|
|
#include "HarbourJson.h"
|
|
|
|
#include "HarbourDebug.h"
|
|
|
|
|
|
|
|
#include "ZLImage.h"
|
|
|
|
#include "image/ZLQtImageManager.h"
|
|
|
|
#include "bookmodel/BookModel.h"
|
|
|
|
#include "library/Author.h"
|
|
|
|
|
|
|
|
#include <QFile>
|
2015-07-23 02:30:30 +03:00
|
|
|
#include <QFileInfo>
|
2015-06-28 14:22:35 +03:00
|
|
|
#include <QDirIterator>
|
|
|
|
#include <QGuiApplication>
|
|
|
|
#include <QScreen>
|
|
|
|
|
|
|
|
#define BOOK_STATE_POSITION "position"
|
|
|
|
#define BOOK_COVER_SUFFIX ".cover."
|
|
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
// BooksBook::CoverContext
|
|
|
|
// ==========================================================================
|
|
|
|
class BooksBook::CoverPaintContext : public BooksPaintContext {
|
|
|
|
public:
|
|
|
|
CoverPaintContext();
|
|
|
|
|
|
|
|
void drawImage(int x, int y, const ZLImageData& image);
|
|
|
|
void drawImage(int x, int y, const ZLImageData& image, int width, int height, ScalingType type);
|
|
|
|
void handleImage(const ZLImageData& image);
|
|
|
|
bool gotIt() const;
|
|
|
|
|
|
|
|
public:
|
|
|
|
static QSize gMaxScreenSize;
|
|
|
|
static bool gMaxScreenSizeKnown;
|
|
|
|
|
|
|
|
QImage iImage;
|
|
|
|
};
|
|
|
|
|
|
|
|
QSize BooksBook::CoverPaintContext::gMaxScreenSize(480,640);
|
|
|
|
bool BooksBook::CoverPaintContext::gMaxScreenSizeKnown = false;
|
|
|
|
|
|
|
|
BooksBook::CoverPaintContext::CoverPaintContext()
|
|
|
|
{
|
|
|
|
if (!gMaxScreenSizeKnown) {
|
|
|
|
QList<QScreen*> screens = qGuiApp->screens();
|
|
|
|
const int n = screens.count();
|
|
|
|
for (int i=0; i<n; i++) {
|
|
|
|
QSize s = screens.at(i)->size();
|
|
|
|
gMaxScreenSize.setWidth(qMax(s.width(), gMaxScreenSize.width()));
|
|
|
|
gMaxScreenSize.setHeight(qMax(s.width(), gMaxScreenSize.height()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setWidth(gMaxScreenSize.width());
|
|
|
|
setHeight(gMaxScreenSize.height());
|
|
|
|
}
|
|
|
|
|
|
|
|
void BooksBook::CoverPaintContext::drawImage(int x, int y, const ZLImageData& image)
|
|
|
|
{
|
|
|
|
handleImage(image);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BooksBook::CoverPaintContext::drawImage(int x, int y, const ZLImageData& image,
|
|
|
|
int width, int height, ScalingType type)
|
|
|
|
{
|
|
|
|
handleImage(image);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BooksBook::CoverPaintContext::handleImage(const ZLImageData& image)
|
|
|
|
{
|
|
|
|
const QImage* qImage = ((ZLQtImageData&)image).image();
|
|
|
|
HDEBUG(image.width() << 'x' << image.height());
|
|
|
|
if (qImage->height() > qImage->width() &&
|
|
|
|
qImage->width() > iImage.width() &&
|
|
|
|
qImage->height() > iImage.height()) {
|
|
|
|
iImage = *qImage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool BooksBook::CoverPaintContext::gotIt() const
|
|
|
|
{
|
|
|
|
return iImage.width() >= 50 &&
|
|
|
|
iImage.height() >= 80 &&
|
|
|
|
iImage.height() > iImage.width();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
// BooksBook::CoverTask
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
|
|
class BooksBook::CoverTask : public BooksTask
|
|
|
|
{
|
|
|
|
public:
|
2015-11-08 14:20:25 +03:00
|
|
|
CoverTask(QString aStateDir, shared_ptr<Book> aBook) :
|
|
|
|
iStateDir(aStateDir), iBook(aBook), iCoverMissing(false) {}
|
2015-06-28 14:22:35 +03:00
|
|
|
|
|
|
|
bool hasImage() const;
|
2015-07-06 19:26:56 +03:00
|
|
|
QString cachedImagePath() const;
|
2015-06-28 14:22:35 +03:00
|
|
|
|
|
|
|
public:
|
2015-11-08 14:20:25 +03:00
|
|
|
QString iStateDir;
|
2015-06-28 14:22:35 +03:00
|
|
|
shared_ptr<Book> iBook;
|
|
|
|
QImage iCoverImage;
|
|
|
|
bool iCoverMissing;
|
|
|
|
};
|
|
|
|
|
|
|
|
inline bool BooksBook::CoverTask::hasImage() const
|
|
|
|
{
|
|
|
|
return iCoverImage.width() > 0 && iCoverImage.height() > 0;
|
|
|
|
}
|
|
|
|
|
2015-07-06 19:26:56 +03:00
|
|
|
QString BooksBook::CoverTask::cachedImagePath() const
|
|
|
|
{
|
2015-11-08 14:20:25 +03:00
|
|
|
if (!iStateDir.isEmpty()) {
|
|
|
|
return iStateDir + "/" +
|
2015-07-06 19:26:56 +03:00
|
|
|
QString::fromStdString(iBook->file().name(false)) +
|
|
|
|
BOOK_COVER_SUFFIX + "jpg";
|
|
|
|
}
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
2015-06-28 14:22:35 +03:00
|
|
|
// ==========================================================================
|
|
|
|
// BooksBook::LoadCoverTask
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
|
|
class BooksBook::LoadCoverTask : public BooksBook::CoverTask
|
|
|
|
{
|
|
|
|
public:
|
2015-11-08 14:20:25 +03:00
|
|
|
LoadCoverTask(QString aStateDir, shared_ptr<Book> aBook,
|
2015-06-28 14:22:35 +03:00
|
|
|
shared_ptr<FormatPlugin> aFormatPlugin) :
|
2015-11-08 14:20:25 +03:00
|
|
|
BooksBook::CoverTask(aStateDir, aBook),
|
2015-06-28 14:22:35 +03:00
|
|
|
iFormatPlugin(aFormatPlugin) {}
|
|
|
|
|
|
|
|
virtual void performTask();
|
|
|
|
|
|
|
|
public:
|
|
|
|
shared_ptr<FormatPlugin> iFormatPlugin;
|
|
|
|
};
|
|
|
|
|
|
|
|
void BooksBook::LoadCoverTask::performTask()
|
|
|
|
{
|
|
|
|
if (!isCanceled()) {
|
|
|
|
// Try to load cached (or custom) cover
|
2015-11-08 14:20:25 +03:00
|
|
|
if (!iStateDir.isEmpty()) {
|
2015-06-28 14:22:35 +03:00
|
|
|
QString coverPrefix(QString::fromStdString(
|
|
|
|
iBook->file().name(false)) + BOOK_COVER_SUFFIX);
|
2015-11-08 14:20:25 +03:00
|
|
|
QDirIterator it(iStateDir);
|
2015-06-28 14:22:35 +03:00
|
|
|
while (it.hasNext()) {
|
|
|
|
QString path(it.next());
|
|
|
|
if (it.fileName().startsWith(coverPrefix)) {
|
|
|
|
if (QFile(path).size() == 0) {
|
|
|
|
HDEBUG("no cover for" << iBook->title().c_str());
|
|
|
|
iCoverMissing = true;
|
|
|
|
return;
|
|
|
|
} else if (iCoverImage.load(path)) {
|
|
|
|
HDEBUG("loaded cover" << iCoverImage.width() << 'x' <<
|
|
|
|
iCoverImage.height() << "for" <<
|
|
|
|
iBook->title().c_str() << "from" <<
|
|
|
|
qPrintable(path));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// OK, fetch one from the book file
|
|
|
|
shared_ptr<ZLImageData> imageData;
|
|
|
|
shared_ptr<ZLImage> image = iFormatPlugin->coverImage(iBook->file());
|
|
|
|
if (!image.isNull()) {
|
|
|
|
imageData = ZLImageManager::Instance().imageData(*image);
|
|
|
|
}
|
|
|
|
if (!imageData.isNull()) {
|
|
|
|
QImage* qImage = (QImage*)((ZLQtImageData&)*imageData).image();
|
|
|
|
if (qImage) iCoverImage = *qImage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#if HARBOUR_DEBUG
|
|
|
|
if (hasImage()) {
|
|
|
|
HDEBUG("loaded cover" << iCoverImage.width() << 'x' <<
|
|
|
|
iCoverImage.height() << "for" << iBook->title().c_str());
|
|
|
|
} else if (isCanceled()) {
|
|
|
|
HDEBUG("cancelled" << iBook->title().c_str());
|
|
|
|
} else {
|
|
|
|
HDEBUG("no cover found in" << iBook->title().c_str());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
// BooksBook::GuessCoverTask
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
|
|
class BooksBook::GuessCoverTask : public BooksBook::CoverTask
|
|
|
|
{
|
|
|
|
public:
|
2015-11-08 14:20:25 +03:00
|
|
|
GuessCoverTask(QString aStateDir, shared_ptr<Book> aBook) :
|
|
|
|
BooksBook::CoverTask(aStateDir, aBook) {}
|
2015-06-28 14:22:35 +03:00
|
|
|
|
|
|
|
virtual void performTask();
|
|
|
|
};
|
|
|
|
|
|
|
|
void BooksBook::GuessCoverTask::performTask()
|
|
|
|
{
|
|
|
|
if (!isCanceled()) {
|
|
|
|
BooksMargins margins;
|
|
|
|
CoverPaintContext context;
|
|
|
|
BookModel bookModel(iBook);
|
|
|
|
shared_ptr<ZLTextModel> model(bookModel.bookTextModel());
|
|
|
|
BooksTextView view(context, BooksTextStyle::defaults(), margins);
|
|
|
|
view.setModel(model);
|
|
|
|
view.rewind();
|
|
|
|
if (!isCanceled()) {
|
|
|
|
view.rewind();
|
|
|
|
if (!isCanceled()) {
|
|
|
|
view.paint();
|
|
|
|
iCoverImage = context.iImage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hasImage()) {
|
|
|
|
HDEBUG("using image" << iCoverImage.width() << 'x' <<
|
|
|
|
iCoverImage.width() << "as cover for" << iBook->title().c_str());
|
|
|
|
|
|
|
|
// Save the extracted image
|
2015-07-06 19:26:56 +03:00
|
|
|
QString coverPath(cachedImagePath());
|
2015-11-08 14:20:25 +03:00
|
|
|
if (!coverPath.isEmpty()) {
|
|
|
|
QFileInfo file(coverPath);
|
|
|
|
QDir dir(file.dir());
|
|
|
|
if (!dir.mkpath(dir.absolutePath())) {
|
|
|
|
HWARN("failed to create" << qPrintable(dir.absolutePath()));
|
|
|
|
}
|
|
|
|
if (iCoverImage.save(coverPath)) {
|
|
|
|
HDEBUG("saved cover to" << qPrintable(coverPath));
|
|
|
|
} else {
|
|
|
|
HWARN("failed to save" << qPrintable(coverPath));
|
|
|
|
}
|
2015-06-28 14:22:35 +03:00
|
|
|
}
|
|
|
|
} else if (isCanceled()) {
|
|
|
|
HDEBUG("cancelled" << iBook->title().c_str());
|
|
|
|
} else {
|
|
|
|
HDEBUG("no cover for" << iBook->title().c_str());
|
|
|
|
iCoverMissing = true;
|
|
|
|
|
|
|
|
// Create empty file. Guessing the cover image is an expensive task,
|
|
|
|
// we don't want to do it every time the application is started.
|
2015-07-06 19:26:56 +03:00
|
|
|
QString coverPath(cachedImagePath());
|
2015-06-28 14:22:35 +03:00
|
|
|
if (!coverPath.isEmpty() &&
|
|
|
|
// Check if the book file still exists - the failure could've
|
|
|
|
// been caused by the SD-card removal.
|
|
|
|
QFile::exists(QString::fromStdString(iBook->file().path()))) {
|
|
|
|
QFile(coverPath).open(QIODevice::WriteOnly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ==========================================================================
|
|
|
|
// BooksBook
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
|
|
// This constructor isn't really used, but is required by qmlRegisterType
|
|
|
|
BooksBook::BooksBook(QObject* aParent) :
|
|
|
|
QObject(aParent),
|
|
|
|
iRef(-1)
|
|
|
|
{
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
2015-11-08 14:20:25 +03:00
|
|
|
BooksBook::BooksBook(const BooksStorage& aStorage, QString aRelativePath,
|
|
|
|
shared_ptr<Book> aBook) :
|
2015-06-28 14:22:35 +03:00
|
|
|
QObject(NULL),
|
|
|
|
iRef(1),
|
|
|
|
iStorage(aStorage),
|
|
|
|
iBook(aBook),
|
|
|
|
iTaskQueue(BooksTaskQueue::instance())
|
|
|
|
{
|
|
|
|
init();
|
|
|
|
HASSERT(!iBook.isNull());
|
|
|
|
iTitle = QString::fromStdString(iBook->title());
|
2015-07-15 22:59:00 +03:00
|
|
|
iPath = QString::fromStdString(iBook->file().physicalFilePath());
|
2015-07-23 02:30:30 +03:00
|
|
|
iFileName = QFileInfo(iPath).fileName();
|
2015-06-28 14:22:35 +03:00
|
|
|
iFormatPlugin = PluginCollection::Instance().plugin(*iBook);
|
|
|
|
AuthorList authors(iBook->authors());
|
|
|
|
const int n = authors.size();
|
|
|
|
for (int i=0; i<n; i++) {
|
|
|
|
if (i > 0) iAuthors += ", ";
|
|
|
|
iAuthors += QString::fromStdString(authors[i]->name());
|
|
|
|
}
|
|
|
|
if (iStorage.isValid()) {
|
2015-11-08 14:20:25 +03:00
|
|
|
iStateDir = iStorage.configDir().path() + "/" + aRelativePath;
|
|
|
|
iStateFilePath = iStateDir + "/" + iFileName + BOOKS_STATE_FILE_SUFFIX;
|
2015-06-28 14:22:35 +03:00
|
|
|
// Load the state
|
|
|
|
QVariantMap state;
|
|
|
|
if (HarbourJson::load(iStateFilePath, state)) {
|
|
|
|
iLastPos = BooksPos::fromVariant(state.value(BOOK_STATE_POSITION));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Refcounted BooksBook objects are managed by C++ code
|
|
|
|
QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BooksBook::init()
|
|
|
|
{
|
|
|
|
iCoverTask = NULL;
|
|
|
|
iCoverTasksDone = false;
|
|
|
|
iCopyingOut = false;
|
|
|
|
iSaveTimer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
BooksBook::~BooksBook()
|
|
|
|
{
|
|
|
|
HDEBUG(qPrintable(iPath));
|
|
|
|
HASSERT(!iRef.load());
|
|
|
|
if (iCoverTask) iCoverTask->release(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
BooksItem* BooksBook::retain()
|
|
|
|
{
|
|
|
|
if (iRef.load() >= 0) {
|
|
|
|
iRef.ref();
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BooksBook::release()
|
|
|
|
{
|
|
|
|
if (iRef.load() >= 0 && !iRef.deref()) {
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QObject* BooksBook::object()
|
|
|
|
{
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
BooksShelf* BooksBook::shelf()
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
BooksBook* BooksBook::book()
|
|
|
|
{
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString BooksBook::name() const
|
|
|
|
{
|
|
|
|
return iTitle;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString BooksBook::fileName() const
|
|
|
|
{
|
|
|
|
return iFileName;
|
|
|
|
}
|
|
|
|
|
2015-11-08 14:20:25 +03:00
|
|
|
bool BooksBook::accessible() const
|
|
|
|
{
|
|
|
|
return !iCopyingOut;
|
|
|
|
}
|
|
|
|
|
2015-06-28 14:22:35 +03:00
|
|
|
void BooksBook::setLastPos(const BooksPos& aPos)
|
|
|
|
{
|
|
|
|
if (iLastPos != aPos) {
|
|
|
|
iLastPos = aPos;
|
|
|
|
// We only need save timer if we have the state file
|
|
|
|
if (!iSaveTimer && iStorage.isValid()) {
|
|
|
|
iSaveTimer = new BooksSaveTimer(this);
|
|
|
|
connect(iSaveTimer, SIGNAL(save()), SLOT(saveState()));
|
|
|
|
}
|
|
|
|
if (iSaveTimer) iSaveTimer->requestSave();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BooksBook::setCopyingOut(bool aValue)
|
|
|
|
{
|
|
|
|
if (iCopyingOut != aValue) {
|
|
|
|
const bool wasAccessible = accessible();
|
|
|
|
iCopyingOut = aValue;
|
|
|
|
Q_EMIT copyingOutChanged();
|
|
|
|
if (wasAccessible != accessible()) {
|
|
|
|
Q_EMIT accessibleChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QImage BooksBook::coverImage()
|
|
|
|
{
|
|
|
|
return iCoverImage;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool BooksBook::hasCoverImage() const
|
|
|
|
{
|
|
|
|
return iCoverImage.width() > 0 && iCoverImage.height() > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BooksBook::setCoverImage(QImage aImage)
|
|
|
|
{
|
|
|
|
if (iCoverImage != aImage) {
|
|
|
|
iCoverImage = aImage;
|
|
|
|
Q_EMIT coverImageChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool BooksBook::requestCoverImage()
|
|
|
|
{
|
|
|
|
if (!iBook.isNull() && !iFormatPlugin.isNull() &&
|
|
|
|
!iCoverTasksDone && !iCoverTask) {
|
|
|
|
HDEBUG(iTitle);
|
2015-11-08 14:20:25 +03:00
|
|
|
iCoverTask = new LoadCoverTask(iStateDir, iBook, iFormatPlugin);
|
2015-06-28 14:22:35 +03:00
|
|
|
connect(iCoverTask, SIGNAL(done()), SLOT(onLoadCoverTaskDone()));
|
|
|
|
iTaskQueue->submit(iCoverTask);
|
|
|
|
Q_EMIT loadingCoverChanged();
|
|
|
|
}
|
|
|
|
return iCoverTask != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BooksBook::cancelCoverRequest()
|
|
|
|
{
|
|
|
|
if (iCoverTask) {
|
|
|
|
iCoverTask->release(this);
|
|
|
|
iCoverTask = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool BooksBook::coverTaskDone()
|
|
|
|
{
|
|
|
|
HASSERT(sender() == iCoverTask);
|
|
|
|
HASSERT(!iCoverTasksDone);
|
|
|
|
HDEBUG(iTitle << iCoverTask->hasImage());
|
|
|
|
const bool gotCover = iCoverTask->hasImage();
|
|
|
|
if (gotCover) {
|
|
|
|
iCoverImage = iCoverTask->iCoverImage;
|
|
|
|
Q_EMIT coverImageChanged();
|
|
|
|
}
|
|
|
|
iCoverTask->release(this);
|
|
|
|
iCoverTask = NULL;
|
|
|
|
return gotCover;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BooksBook::onLoadCoverTaskDone()
|
|
|
|
{
|
|
|
|
HDEBUG(iTitle);
|
|
|
|
const bool coverMissing = iCoverTask->iCoverMissing;
|
|
|
|
if (coverTaskDone() || coverMissing) {
|
|
|
|
iCoverTasksDone = true;
|
|
|
|
Q_EMIT loadingCoverChanged();
|
|
|
|
} else {
|
2015-11-08 14:20:25 +03:00
|
|
|
iCoverTask = new GuessCoverTask(iStateDir, iBook);
|
2015-06-28 14:22:35 +03:00
|
|
|
connect(iCoverTask, SIGNAL(done()), SLOT(onGuessCoverTaskDone()));
|
|
|
|
iTaskQueue->submit(iCoverTask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BooksBook::onGuessCoverTaskDone()
|
|
|
|
{
|
|
|
|
HDEBUG(iTitle);
|
2015-07-06 19:26:56 +03:00
|
|
|
|
|
|
|
if (iCoverTask->iCoverMissing && !iCoverImage.isNull()) {
|
|
|
|
// This may happen after the book with custom cover has been moved
|
|
|
|
// between SD-card and the internal storage. Store the custom cover
|
|
|
|
// so that it doesn't get lost. This is a fairly rare situation.
|
|
|
|
QString coverPath(iCoverTask->cachedImagePath());
|
|
|
|
if (!coverPath.isEmpty() && iCoverImage.save(coverPath)) {
|
|
|
|
HDEBUG("saved custom cover to" << qPrintable(coverPath));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-28 14:22:35 +03:00
|
|
|
coverTaskDone();
|
|
|
|
iCoverTasksDone = true;
|
|
|
|
Q_EMIT loadingCoverChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void BooksBook::saveState()
|
|
|
|
{
|
|
|
|
if (!iStateFilePath.isEmpty()) {
|
|
|
|
QVariantMap state;
|
|
|
|
HarbourJson::load(iStateFilePath, state);
|
|
|
|
state.insert(BOOK_STATE_POSITION, iLastPos.toVariant());
|
|
|
|
if (HarbourJson::save(iStateFilePath, state)) {
|
|
|
|
HDEBUG("wrote" << iStateFilePath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BooksBook::deleteFiles()
|
|
|
|
{
|
|
|
|
if (iStorage.isValid()) {
|
|
|
|
if (QFile::remove(iPath)) {
|
|
|
|
HDEBUG("deleted" << qPrintable(iPath));
|
|
|
|
} else {
|
|
|
|
HWARN("failed to delete" << qPrintable(iPath));
|
|
|
|
}
|
2015-11-08 14:20:25 +03:00
|
|
|
QDirIterator it(iStateDir);
|
2015-06-28 14:22:35 +03:00
|
|
|
while (it.hasNext()) {
|
|
|
|
QString path(it.next());
|
|
|
|
if (it.fileName().startsWith(iFileName)) {
|
|
|
|
if (QFile::remove(path)) {
|
|
|
|
HDEBUG(qPrintable(path));
|
|
|
|
} else {
|
|
|
|
HWARN("failed to delete" << qPrintable(path));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|