[app] Handle the case if hash can't be stored as fs attr (e.g. SD-card)
Assume that the hash from page marks file is correct and verify it later when the file hash gets recalculated.
This commit is contained in:
parent
01987a8fa5
commit
28a814dd26
2 changed files with 100 additions and 34 deletions
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
#include "BooksBookModel.h"
|
#include "BooksBookModel.h"
|
||||||
#include "BooksTextStyle.h"
|
#include "BooksTextStyle.h"
|
||||||
|
#include "BooksUtil.h"
|
||||||
|
|
||||||
#include "HarbourDebug.h"
|
#include "HarbourDebug.h"
|
||||||
|
|
||||||
|
@ -51,6 +52,7 @@ public:
|
||||||
int iHeight;
|
int iHeight;
|
||||||
shared_ptr<BookModel> iBookModel;
|
shared_ptr<BookModel> iBookModel;
|
||||||
BooksPos::List iPageMarks;
|
BooksPos::List iPageMarks;
|
||||||
|
QByteArray iHash;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
@ -82,8 +84,9 @@ public:
|
||||||
void performTask();
|
void performTask();
|
||||||
|
|
||||||
static QString pageMarksFile(BooksBookModel* aModel);
|
static QString pageMarksFile(BooksBookModel* aModel);
|
||||||
BooksPos::List loadPageMarks();
|
BooksPos::List loadPageMarks() const;
|
||||||
void savePageMarks();
|
bool acceptHash(const MarksHeader* aHeader) const;
|
||||||
|
void savePageMarks() const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void progress(int aProgress);
|
void progress(int aProgress);
|
||||||
|
@ -95,7 +98,8 @@ public:
|
||||||
const BooksMargins iMargins;
|
const BooksMargins iMargins;
|
||||||
const QString iPageMarksFile;
|
const QString iPageMarksFile;
|
||||||
const QByteArray iHash;
|
const QByteArray iHash;
|
||||||
BooksBookModel::Data* iData;
|
const QString iPath;
|
||||||
|
Data* iData;
|
||||||
};
|
};
|
||||||
|
|
||||||
const char BooksBookModel::PagingTask::MarksFileMagic[] = "MARK";
|
const char BooksBookModel::PagingTask::MarksFileMagic[] = "MARK";
|
||||||
|
@ -108,6 +112,7 @@ BooksBookModel::PagingTask::PagingTask(BooksBookModel* aModel,
|
||||||
iMargins(aModel->margins()),
|
iMargins(aModel->margins()),
|
||||||
iPageMarksFile(pageMarksFile(aModel)),
|
iPageMarksFile(pageMarksFile(aModel)),
|
||||||
iHash(aModel->book()->hash()),
|
iHash(aModel->book()->hash()),
|
||||||
|
iPath(aModel->book()->path()),
|
||||||
iData(NULL)
|
iData(NULL)
|
||||||
{
|
{
|
||||||
aModel->connect(this, SIGNAL(done()), SLOT(onResetDone()));
|
aModel->connect(this, SIGNAL(done()), SLOT(onResetDone()));
|
||||||
|
@ -126,11 +131,20 @@ QString BooksBookModel::PagingTask::pageMarksFile(BooksBookModel* aModel)
|
||||||
arg(aModel->width()).arg(aModel->height()));
|
arg(aModel->width()).arg(aModel->height()));
|
||||||
}
|
}
|
||||||
|
|
||||||
BooksPos::List BooksBookModel::PagingTask::loadPageMarks()
|
bool BooksBookModel::PagingTask::acceptHash(const MarksHeader* aHeader) const
|
||||||
|
{
|
||||||
|
// If the real hash is unknown, we accept any. The real one will
|
||||||
|
// be later compared with the one we fetch from the .marks file
|
||||||
|
return iHash.isEmpty() ||
|
||||||
|
(iHash.size() == sizeof(aHeader->hash) &&
|
||||||
|
!memcmp(iHash.constData(), aHeader->hash, sizeof(aHeader->hash)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BooksPos::List BooksBookModel::PagingTask::loadPageMarks() const
|
||||||
{
|
{
|
||||||
BooksPos::List list;
|
BooksPos::List list;
|
||||||
QFile file(iPageMarksFile);
|
QFile file(iPageMarksFile);
|
||||||
if (!iHash.isEmpty() && file.open(QIODevice::ReadOnly)) {
|
if (file.open(QIODevice::ReadOnly)) {
|
||||||
const qint64 size = file.size();
|
const qint64 size = file.size();
|
||||||
uchar* map = file.map(0, size);
|
uchar* map = file.map(0, size);
|
||||||
if (map) {
|
if (map) {
|
||||||
|
@ -140,8 +154,7 @@ BooksPos::List BooksBookModel::PagingTask::loadPageMarks()
|
||||||
const MarksHeader* hdr = (MarksHeader*)map;
|
const MarksHeader* hdr = (MarksHeader*)map;
|
||||||
if (!memcmp(hdr->magic, MarksFileMagic, sizeof(hdr->magic)) &&
|
if (!memcmp(hdr->magic, MarksFileMagic, sizeof(hdr->magic)) &&
|
||||||
hdr->version == MarksFileVersion &&
|
hdr->version == MarksFileVersion &&
|
||||||
iHash.size() == sizeof(hdr->hash) &&
|
acceptHash(hdr) &&
|
||||||
!memcmp(iHash.constData(), hdr->hash, sizeof(hdr->hash)) &&
|
|
||||||
hdr->fontSize == iTextStyle->fontSize() &&
|
hdr->fontSize == iTextStyle->fontSize() &&
|
||||||
hdr->leftMargin == iMargins.iLeft &&
|
hdr->leftMargin == iMargins.iLeft &&
|
||||||
hdr->rightMargin == iMargins.iRight &&
|
hdr->rightMargin == iMargins.iRight &&
|
||||||
|
@ -184,20 +197,30 @@ BooksPos::List BooksBookModel::PagingTask::loadPageMarks()
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooksBookModel::PagingTask::savePageMarks()
|
void BooksBookModel::PagingTask::savePageMarks() const
|
||||||
{
|
{
|
||||||
MarksHeader hdr;
|
MarksHeader hdr;
|
||||||
HASSERT(iHash.size() == sizeof(hdr.hash));
|
QByteArray hash(iData->iHash);
|
||||||
if (iHash.size() == sizeof(hdr.hash) &&
|
if (hash.size() == sizeof(hdr.hash) &&
|
||||||
!iData->iPageMarks.isEmpty()) {
|
!iData->iPageMarks.isEmpty() &&
|
||||||
|
!isCanceled()) {
|
||||||
QFile file(iPageMarksFile);
|
QFile file(iPageMarksFile);
|
||||||
if (file.open(QIODevice::ReadWrite)) {
|
bool opened = file.open(QIODevice::ReadWrite);
|
||||||
|
if (!opened) {
|
||||||
|
// Most likely, the directory doesn't exist
|
||||||
|
QDir dir = QFileInfo(iPageMarksFile).dir();
|
||||||
|
if (dir.mkpath(dir.path())) {
|
||||||
|
HDEBUG("created" << qPrintable(dir.path()));
|
||||||
|
opened = file.open(QIODevice::ReadWrite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opened) {
|
||||||
HDEBUG("writing" << qPrintable(iPageMarksFile));
|
HDEBUG("writing" << qPrintable(iPageMarksFile));
|
||||||
const int n = iData->iPageMarks.count();
|
const int n = iData->iPageMarks.count();
|
||||||
memset(&hdr, 0, sizeof(hdr));
|
memset(&hdr, 0, sizeof(hdr));
|
||||||
memcpy(hdr.magic, MarksFileMagic, sizeof(hdr.magic));
|
memcpy(hdr.magic, MarksFileMagic, sizeof(hdr.magic));
|
||||||
hdr.version = MarksFileVersion;
|
hdr.version = MarksFileVersion;
|
||||||
memcpy(hdr.hash, iHash.constData(), sizeof(hdr.hash));
|
memcpy(hdr.hash, hash.constData(), sizeof(hdr.hash));
|
||||||
hdr.fontSize = iTextStyle->fontSize();
|
hdr.fontSize = iTextStyle->fontSize();
|
||||||
hdr.leftMargin = iMargins.iLeft;
|
hdr.leftMargin = iMargins.iLeft;
|
||||||
hdr.rightMargin = iMargins.iRight;
|
hdr.rightMargin = iMargins.iRight;
|
||||||
|
@ -223,11 +246,17 @@ void BooksBookModel::PagingTask::savePageMarks()
|
||||||
void BooksBookModel::PagingTask::performTask()
|
void BooksBookModel::PagingTask::performTask()
|
||||||
{
|
{
|
||||||
if (!isCanceled()) {
|
if (!isCanceled()) {
|
||||||
iData = new BooksBookModel::Data(iPaint.width(), iPaint.height());
|
iData = new Data(iPaint.width(), iPaint.height());
|
||||||
iData->iBookModel = new BookModel(iBook);
|
iData->iBookModel = new BookModel(iBook);
|
||||||
|
iData->iHash = iHash;
|
||||||
shared_ptr<ZLTextModel> model(iData->iBookModel->bookTextModel());
|
shared_ptr<ZLTextModel> model(iData->iBookModel->bookTextModel());
|
||||||
ZLTextHyphenator::Instance().load(iBook->language());
|
ZLTextHyphenator::Instance().load(iBook->language());
|
||||||
if (!isCanceled()) {
|
if (iData->iHash.isEmpty() && !isCanceled()) {
|
||||||
|
// If hash is unknown then we need to compute it here and now.
|
||||||
|
// It's a very rare occasion though.
|
||||||
|
iData->iHash = BooksUtil::computeFileHashAndSetAttr(iPath, this);
|
||||||
|
}
|
||||||
|
if (!iData->iHash.isEmpty() && !isCanceled()) {
|
||||||
// Load the cached marks
|
// Load the cached marks
|
||||||
iData->iPageMarks = loadPageMarks();
|
iData->iPageMarks = loadPageMarks();
|
||||||
if (iData->iPageMarks.isEmpty() && !isCanceled()) {
|
if (iData->iPageMarks.isEmpty() && !isCanceled()) {
|
||||||
|
@ -321,6 +350,7 @@ void BooksBookModel::setBook(BooksBook* aBook)
|
||||||
iTextStyle = iSettings->textStyle(fontSizeAdjust());
|
iTextStyle = iSettings->textStyle(fontSizeAdjust());
|
||||||
iPageStack->setStack(aBook->pageStack(), aBook->pageStackPos());
|
iPageStack->setStack(aBook->pageStack(), aBook->pageStackPos());
|
||||||
connect(aBook, SIGNAL(fontSizeAdjustChanged()), SLOT(onTextStyleChanged()));
|
connect(aBook, SIGNAL(fontSizeAdjustChanged()), SLOT(onTextStyleChanged()));
|
||||||
|
connect(aBook, SIGNAL(hashChanged()), SLOT(onHashChanged()));
|
||||||
HDEBUG(iTitle);
|
HDEBUG(iTitle);
|
||||||
} else {
|
} else {
|
||||||
iBook = NULL;
|
iBook = NULL;
|
||||||
|
@ -363,6 +393,34 @@ void BooksBookModel::onPageStackChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BooksBookModel::onHashChanged()
|
||||||
|
{
|
||||||
|
const QByteArray hash(iBook->hash());
|
||||||
|
HDEBUG(QString(hash.toHex()));
|
||||||
|
if (!hash.isEmpty()) {
|
||||||
|
if (iData2 && iData2->iHash != hash) {
|
||||||
|
// There is no need to delete the stale file - it will be deleted
|
||||||
|
// by the paging task. Deleting files on the UI thread is not a
|
||||||
|
// very bright idea - the call may block for quite some time.
|
||||||
|
delete iData2;
|
||||||
|
iData2 = NULL;
|
||||||
|
}
|
||||||
|
if (iPagingTask &&
|
||||||
|
!iPagingTask->iHash.isEmpty() &&
|
||||||
|
iPagingTask->iHash != hash) {
|
||||||
|
iPagingTask->release(this);
|
||||||
|
iPagingTask = NULL;
|
||||||
|
startReset(iResetReason);
|
||||||
|
} else if (iData && iData->iHash != hash) {
|
||||||
|
delete iData;
|
||||||
|
iData = NULL;
|
||||||
|
startReset(ReasonLoading);
|
||||||
|
} else {
|
||||||
|
HDEBUG("we are all set!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int BooksBookModel::pageCount() const
|
int BooksBookModel::pageCount() const
|
||||||
{
|
{
|
||||||
return iData ? iData->iPageMarks.count() : 0;
|
return iData ? iData->iPageMarks.count() : 0;
|
||||||
|
@ -600,6 +658,8 @@ void BooksBookModel::onResetDone()
|
||||||
HASSERT(iPagingTask->iData);
|
HASSERT(iPagingTask->iData);
|
||||||
HASSERT(!iData);
|
HASSERT(!iData);
|
||||||
|
|
||||||
|
const QByteArray hash(iBook->hash());
|
||||||
|
if (hash.isEmpty() || iPagingTask->iData->iHash == hash) {
|
||||||
const int oldPageCount(pageCount());
|
const int oldPageCount(pageCount());
|
||||||
shared_ptr<BookModel> oldBookModel(bookModel());
|
shared_ptr<BookModel> oldBookModel(bookModel());
|
||||||
BooksLoadingSignalBlocker block(this);
|
BooksLoadingSignalBlocker block(this);
|
||||||
|
@ -620,6 +680,11 @@ void BooksBookModel::onResetDone()
|
||||||
iResetReason = ReasonUnknown;
|
iResetReason = ReasonUnknown;
|
||||||
Q_EMIT resetReasonChanged();
|
Q_EMIT resetReasonChanged();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
HDEBUG("oops");
|
||||||
|
iPagingTask->release(this);
|
||||||
|
iPagingTask = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int,QByteArray> BooksBookModel::roleNames() const
|
QHash<int,QByteArray> BooksBookModel::roleNames() const
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015-2017 Jolla Ltd.
|
* Copyright (C) 2015-2018 Jolla Ltd.
|
||||||
* Contact: Slava Monich <slava.monich@jolla.com>
|
* Copyright (C) 2015-2018 Slava Monich <slava.monich@jolla.com>
|
||||||
*
|
*
|
||||||
* You may use this file under the terms of the BSD license as follows:
|
* You may use this file under the terms of the BSD license as follows:
|
||||||
*
|
*
|
||||||
|
@ -139,6 +139,7 @@ private Q_SLOTS:
|
||||||
void onResetDone();
|
void onResetDone();
|
||||||
void onTextStyleChanged();
|
void onTextStyleChanged();
|
||||||
void onPageStackChanged();
|
void onPageStackChanged();
|
||||||
|
void onHashChanged();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void sizeChanged();
|
void sizeChanged();
|
||||||
|
|
Loading…
Reference in a new issue