[app] Tweaking book copy

This commit is contained in:
Slava Monich 2015-12-12 11:36:22 +03:00
parent 84b9b77523
commit c95bb79aa2
13 changed files with 230 additions and 117 deletions

View file

@ -138,14 +138,24 @@ Item {
ProgressCircle { ProgressCircle {
id: progressIndicator id: progressIndicator
width: parent.width/2 width: busyIndicator.width
height: width height: width
anchors.centerIn: parent anchors.centerIn: busyIndicator
opacity: (copying && !longCopyTimer.running && value > 0 && value < 1) ? 1 : 0 opacity: (copying && !longCopyTimer.running && value > 0 && value < 1) ? 1 : 0
visible: opacity > 0 visible: opacity > 0
Behavior on opacity { FadeAnimation {} } Behavior on opacity { FadeAnimation {} }
} }
BusyIndicator {
id: busyIndicator
size: BusyIndicatorSize.Medium
x: cover.x + cover.centerX - width/2
y: cover.y + cover.centerY - height/2
visible: opacity > 0
running: copying && !longCopyTimer.running && (progressIndicator.value <= 0 || progressIndicator.value >= 1)
Behavior on opacity { enabled: false }
}
function withinDeleteButton(x, y) { function withinDeleteButton(x, y) {
return x >= deleteButton.x - deleteButton.margin && return x >= deleteButton.x - deleteButton.margin &&
x < deleteButton.x + deleteButton.width + deleteButton.margin && x < deleteButton.x + deleteButton.width + deleteButton.margin &&

View file

@ -37,6 +37,7 @@
#include "BooksTextView.h" #include "BooksTextView.h"
#include "BooksTextStyle.h" #include "BooksTextStyle.h"
#include "BooksPaintContext.h" #include "BooksPaintContext.h"
#include "BooksUtil.h"
#include "HarbourJson.h" #include "HarbourJson.h"
#include "HarbourDebug.h" #include "HarbourDebug.h"
@ -46,6 +47,7 @@
#include "bookmodel/BookModel.h" #include "bookmodel/BookModel.h"
#include "library/Author.h" #include "library/Author.h"
#include <QDir>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QDirIterator> #include <QDirIterator>
@ -132,15 +134,16 @@ bool BooksBook::CoverPaintContext::gotIt() const
class BooksBook::CoverTask : public BooksTask class BooksBook::CoverTask : public BooksTask
{ {
public: public:
CoverTask(QString aStateDir, shared_ptr<Book> aBook) : CoverTask(QString aStateDir, shared_ptr<Book> aBook, QString aImagePath) :
iStateDir(aStateDir), iBook(aBook), iCoverMissing(false) {} iStateDir(aStateDir), iBook(aBook), iImagePath(aImagePath),
iCoverMissing(false) {}
bool hasImage() const; bool hasImage() const;
QString cachedImagePath() const;
public: public:
QString iStateDir; QString iStateDir;
shared_ptr<Book> iBook; shared_ptr<Book> iBook;
QString iImagePath;
QImage iCoverImage; QImage iCoverImage;
bool iCoverMissing; bool iCoverMissing;
}; };
@ -150,16 +153,6 @@ inline bool BooksBook::CoverTask::hasImage() const
return iCoverImage.width() > 0 && iCoverImage.height() > 0; return iCoverImage.width() > 0 && iCoverImage.height() > 0;
} }
QString BooksBook::CoverTask::cachedImagePath() const
{
if (!iStateDir.isEmpty()) {
return iStateDir + "/" +
QString::fromStdString(iBook->file().name(false)) +
BOOK_COVER_SUFFIX + "jpg";
}
return QString();
}
// ========================================================================== // ==========================================================================
// BooksBook::LoadCoverTask // BooksBook::LoadCoverTask
// ========================================================================== // ==========================================================================
@ -168,8 +161,8 @@ class BooksBook::LoadCoverTask : public BooksBook::CoverTask
{ {
public: public:
LoadCoverTask(QString aStateDir, shared_ptr<Book> aBook, LoadCoverTask(QString aStateDir, shared_ptr<Book> aBook,
shared_ptr<FormatPlugin> aFormatPlugin) : shared_ptr<FormatPlugin> aFormatPlugin, QString aImagePath) :
BooksBook::CoverTask(aStateDir, aBook), BooksBook::CoverTask(aStateDir, aBook, aImagePath),
iFormatPlugin(aFormatPlugin) {} iFormatPlugin(aFormatPlugin) {}
virtual void performTask(); virtual void performTask();
@ -234,8 +227,8 @@ void BooksBook::LoadCoverTask::performTask()
class BooksBook::GuessCoverTask : public BooksBook::CoverTask class BooksBook::GuessCoverTask : public BooksBook::CoverTask
{ {
public: public:
GuessCoverTask(QString aStateDir, shared_ptr<Book> aBook) : GuessCoverTask(QString aStateDir, shared_ptr<Book> aBook, QString aImagePath) :
BooksBook::CoverTask(aStateDir, aBook) {} BooksBook::CoverTask(aStateDir, aBook, aImagePath) {}
virtual void performTask(); virtual void performTask();
}; };
@ -264,17 +257,16 @@ void BooksBook::GuessCoverTask::performTask()
iCoverImage.width() << "as cover for" << iBook->title().c_str()); iCoverImage.width() << "as cover for" << iBook->title().c_str());
// Save the extracted image // Save the extracted image
QString coverPath(cachedImagePath()); if (!iImagePath.isEmpty()) {
if (!coverPath.isEmpty()) { QFileInfo file(iImagePath);
QFileInfo file(coverPath);
QDir dir(file.dir()); QDir dir(file.dir());
if (!dir.mkpath(dir.absolutePath())) { if (!dir.mkpath(dir.absolutePath())) {
HWARN("failed to create" << qPrintable(dir.absolutePath())); HWARN("failed to create" << qPrintable(dir.absolutePath()));
} }
if (iCoverImage.save(coverPath)) { if (iCoverImage.save(iImagePath)) {
HDEBUG("saved cover to" << qPrintable(coverPath)); HDEBUG("saved cover to" << qPrintable(iImagePath));
} else { } else {
HWARN("failed to save" << qPrintable(coverPath)); HWARN("failed to save" << qPrintable(iImagePath));
} }
} }
} else if (isCanceled()) { } else if (isCanceled()) {
@ -285,12 +277,11 @@ void BooksBook::GuessCoverTask::performTask()
// Create empty file. Guessing the cover image is an expensive task, // Create empty file. Guessing the cover image is an expensive task,
// we don't want to do it every time the application is started. // we don't want to do it every time the application is started.
QString coverPath(cachedImagePath()); if (!iImagePath.isEmpty() &&
if (!coverPath.isEmpty() &&
// Check if the book file still exists - the failure could've // Check if the book file still exists - the failure could've
// been caused by the SD-card removal. // been caused by the SD-card removal.
QFile::exists(QString::fromStdString(iBook->file().path()))) { QFile::exists(QString::fromStdString(iBook->file().path()))) {
QFile(coverPath).open(QIODevice::WriteOnly); QFile(iImagePath).open(QIODevice::WriteOnly);
} }
} }
} }
@ -313,7 +304,7 @@ BooksBook::BooksBook(const BooksStorage& aStorage, QString aRelativePath,
iRef(1), iRef(1),
iStorage(aStorage), iStorage(aStorage),
iBook(aBook), iBook(aBook),
iTaskQueue(BooksTaskQueue::instance()) iTaskQueue(BooksTaskQueue::defaultQueue())
{ {
init(); init();
HASSERT(!iBook.isNull()); HASSERT(!iBook.isNull());
@ -328,8 +319,10 @@ BooksBook::BooksBook(const BooksStorage& aStorage, QString aRelativePath,
iAuthors += QString::fromStdString(authors[i]->name()); iAuthors += QString::fromStdString(authors[i]->name());
} }
if (iStorage.isValid()) { if (iStorage.isValid()) {
iStateDir = iStorage.configDir().path() + "/" + aRelativePath; iStateDir = QDir::cleanPath(iStorage.configDir().path() +
iStateFilePath = iStateDir + "/" + iFileName + BOOKS_STATE_FILE_SUFFIX; QDir::separator() + aRelativePath);
iStateFilePath = QDir::cleanPath(iStateDir +
QDir::separator() + iFileName + BOOKS_STATE_FILE_SUFFIX);
// Load the state // Load the state
QVariantMap state; QVariantMap state;
if (HarbourJson::load(iStateFilePath, state)) { if (HarbourJson::load(iStateFilePath, state)) {
@ -430,11 +423,6 @@ void BooksBook::setCopyingOut(bool aValue)
} }
} }
QImage BooksBook::coverImage()
{
return iCoverImage;
}
bool BooksBook::hasCoverImage() const bool BooksBook::hasCoverImage() const
{ {
return iCoverImage.width() > 0 && iCoverImage.height() > 0; return iCoverImage.width() > 0 && iCoverImage.height() > 0;
@ -453,7 +441,8 @@ bool BooksBook::requestCoverImage()
if (!iBook.isNull() && !iFormatPlugin.isNull() && if (!iBook.isNull() && !iFormatPlugin.isNull() &&
!iCoverTasksDone && !iCoverTask) { !iCoverTasksDone && !iCoverTask) {
HDEBUG(iTitle); HDEBUG(iTitle);
iCoverTask = new LoadCoverTask(iStateDir, iBook, iFormatPlugin); iCoverTask = new LoadCoverTask(iStateDir, iBook, iFormatPlugin,
cachedImagePath());
connect(iCoverTask, SIGNAL(done()), SLOT(onLoadCoverTaskDone())); connect(iCoverTask, SIGNAL(done()), SLOT(onLoadCoverTaskDone()));
iTaskQueue->submit(iCoverTask); iTaskQueue->submit(iCoverTask);
Q_EMIT loadingCoverChanged(); Q_EMIT loadingCoverChanged();
@ -492,7 +481,7 @@ void BooksBook::onLoadCoverTaskDone()
iCoverTasksDone = true; iCoverTasksDone = true;
Q_EMIT loadingCoverChanged(); Q_EMIT loadingCoverChanged();
} else { } else {
iCoverTask = new GuessCoverTask(iStateDir, iBook); iCoverTask = new GuessCoverTask(iStateDir, iBook, cachedImagePath());
connect(iCoverTask, SIGNAL(done()), SLOT(onGuessCoverTaskDone())); connect(iCoverTask, SIGNAL(done()), SLOT(onGuessCoverTaskDone()));
iTaskQueue->submit(iCoverTask); iTaskQueue->submit(iCoverTask);
} }
@ -501,22 +490,21 @@ void BooksBook::onLoadCoverTaskDone()
void BooksBook::onGuessCoverTaskDone() void BooksBook::onGuessCoverTaskDone()
{ {
HDEBUG(iTitle); HDEBUG(iTitle);
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));
}
}
coverTaskDone(); coverTaskDone();
iCoverTasksDone = true; iCoverTasksDone = true;
Q_EMIT loadingCoverChanged(); Q_EMIT loadingCoverChanged();
} }
QString BooksBook::cachedImagePath() const
{
if (!iStateDir.isEmpty() && !iBook.isNull()) {
return QDir::cleanPath(iStateDir + QDir::separator() +
QString::fromStdString(iBook->file().name(false)) +
BOOK_COVER_SUFFIX "jpg");
}
return QString();
}
void BooksBook::saveState() void BooksBook::saveState()
{ {
if (!iStateFilePath.isEmpty()) { if (!iStateFilePath.isEmpty()) {
@ -551,6 +539,21 @@ void BooksBook::deleteFiles()
} }
} }
BooksBook* BooksBook::newBook(const BooksStorage& aStorage, QString aRelPath,
QString aFileName)
{
shared_ptr<Book> ref = BooksUtil::bookFromFile(
QFileInfo(QDir(aStorage.fullPath(aRelPath)), aFileName).
absoluteFilePath());
if (!ref.isNull()) {
BooksBook* book = new BooksBook(aStorage, aRelPath, ref);
book->moveToThread(qApp->thread());
return book;
} else {
return NULL;
}
}
// NOTE: below methods are invoked on the worker thread // NOTE: below methods are invoked on the worker thread
bool BooksBook::makeLink(QString aDestPath) bool BooksBook::makeLink(QString aDestPath)
{ {
@ -574,21 +577,24 @@ bool BooksBook::makeLink(QString aDestPath)
return false; return false;
} }
bool BooksBook::copyTo(QDir aDestDir, CopyOperation* aOperation) BooksItem* BooksBook::copyTo(const BooksStorage& aStorage, QString aRelPath,
CopyOperation* aOperation)
{ {
QString destPath(QFileInfo(aDestDir, iFileName).absoluteFilePath()); QDir destDir(aStorage.fullPath(aRelPath));
aDestDir.mkpath(aDestDir.path()); destDir.mkpath(destDir.path());
if (!isCanceled(aOperation) && makeLink(destPath)) { const QString absDestPath(QFileInfo(QDir(aStorage.fullPath(aRelPath)),
return true; iFileName).absoluteFilePath());
if (!isCanceled(aOperation) && makeLink(absDestPath)) {
return newBook(aStorage, aRelPath, iFileName);
} else if (isCanceled(aOperation)) { } else if (isCanceled(aOperation)) {
return true; return NULL;
} else { } else {
bool success = false; BooksBook* copy = NULL;
QFile src(iPath); QFile src(iPath);
const qint64 total = src.size(); const qint64 total = src.size();
qint64 copied = 0; qint64 copied = 0;
if (src.open(QIODevice::ReadOnly)) { if (src.open(QIODevice::ReadOnly)) {
QFile dest(destPath); QFile dest(absDestPath);
if (dest.open(QIODevice::WriteOnly)) { if (dest.open(QIODevice::WriteOnly)) {
QDateTime lastTime; QDateTime lastTime;
const qint64 bufsiz = 0x1000; const qint64 bufsiz = 0x1000;
@ -614,32 +620,40 @@ bool BooksBook::copyTo(QDir aDestDir, CopyOperation* aOperation)
} }
} }
} }
aOperation->copyProgressChanged(PROGRESS_PRECISION);
delete [] buf; delete [] buf;
dest.close(); dest.close();
aOperation->copyProgressChanged(PROGRESS_PRECISION);
if (copied == total) { if (copied == total) {
dest.setPermissions(BOOKS_FILE_PERMISSIONS); dest.setPermissions(BOOKS_FILE_PERMISSIONS);
success = true;
HDEBUG(total << "bytes copied from"<< qPrintable(iPath) << HDEBUG(total << "bytes copied from"<< qPrintable(iPath) <<
"to" << qPrintable(destPath)); "to" << qPrintable(absDestPath));
copy = newBook(aStorage, aRelPath, iFileName);
// Copy cover image too
if (copy && !iCoverImage.isNull()) {
QString cover(copy->cachedImagePath());
if (!cover.isEmpty() && iCoverImage.save(cover)) {
HDEBUG("copied cover to" << qPrintable(cover));
}
}
} else { } else {
if (isCanceled(aOperation)) { if (isCanceled(aOperation)) {
HDEBUG("copy" << qPrintable(iPath) << "to" << HDEBUG("copy" << qPrintable(iPath) << "to" <<
qPrintable(destPath) << "cancelled"); qPrintable(absDestPath) << "cancelled");
} else { } else {
HWARN(copied << "out of" << total << HWARN(copied << "out of" << total <<
"bytes copied from" << qPrintable(iPath) << "bytes copied from" << qPrintable(iPath) <<
"to" << qPrintable(destPath)); "to" << qPrintable(absDestPath));
} }
dest.remove(); dest.remove();
} }
} else { } else {
HWARN("failed to open" << qPrintable(destPath)); HWARN("failed to open" << qPrintable(absDestPath));
} }
src.close(); src.close();
} else { } else {
HWARN("failed to open" << qPrintable(iPath)); HWARN("failed to open" << qPrintable(iPath));
} }
return success; return copy;
} }
} }

View file

@ -70,6 +70,9 @@ public:
shared_ptr<Book> aBook); shared_ptr<Book> aBook);
~BooksBook(); ~BooksBook();
static BooksBook* newBook(const BooksStorage& aStorage, QString aRelPath,
QString aFileName);
QString title() const { return iTitle; } QString title() const { return iTitle; }
QString authors() const { return iAuthors; } QString authors() const { return iAuthors; }
BooksPos lastPos() const { return iLastPos; } BooksPos lastPos() const { return iLastPos; }
@ -82,8 +85,7 @@ public:
bool requestCoverImage(); bool requestCoverImage();
void cancelCoverRequest(); void cancelCoverRequest();
void setCoverImage(QImage aImage); void setCoverImage(QImage aImage);
QImage coverImage(); QImage coverImage() const;
void setCopyingOut(bool aValue); void setCopyingOut(bool aValue);
// BooksItem // BooksItem
@ -97,7 +99,8 @@ public:
virtual QString path() const; virtual QString path() const;
virtual bool accessible() const; virtual bool accessible() const;
virtual void deleteFiles(); virtual void deleteFiles();
virtual bool copyTo(QDir aDestDir, CopyOperation* aObserver); virtual BooksItem* copyTo(const BooksStorage& aStorage, QString aRelPath,
CopyOperation* aObserver);
Q_SIGNALS: Q_SIGNALS:
void coverImageChanged(); void coverImageChanged();
@ -115,6 +118,7 @@ private:
void init(); void init();
bool coverTaskDone(); bool coverTaskDone();
bool makeLink(QString aDestPath); bool makeLink(QString aDestPath);
QString cachedImagePath() const;
static bool isCanceled(CopyOperation* aOperation); static bool isCanceled(CopyOperation* aOperation);
private: private:
@ -141,5 +145,7 @@ QML_DECLARE_TYPE(BooksBook)
inline bool BooksBook::isCanceled(CopyOperation* aObserver) inline bool BooksBook::isCanceled(CopyOperation* aObserver)
{ return aObserver && aObserver->isCanceled(); } { return aObserver && aObserver->isCanceled(); }
inline QImage BooksBook::coverImage() const
{ return iCoverImage; }
#endif // BOOKS_BOOK_H #endif // BOOKS_BOOK_H

View file

@ -210,7 +210,7 @@ BooksBookModel::BooksBookModel(QObject* aParent) :
iTask(NULL), iTask(NULL),
iData(NULL), iData(NULL),
iData2(NULL), iData2(NULL),
iTaskQueue(BooksTaskQueue::instance()), iTaskQueue(BooksTaskQueue::defaultQueue()),
iTextStyle(BooksTextStyle::defaults()) iTextStyle(BooksTextStyle::defaults())
{ {
HDEBUG("created"); HDEBUG("created");

View file

@ -198,7 +198,7 @@ void BooksCoverWidget::DefaultImage::release(QImage* aImage)
BooksCoverWidget::BooksCoverWidget(QQuickItem* aParent) : BooksCoverWidget::BooksCoverWidget(QQuickItem* aParent) :
QQuickPaintedItem(aParent), QQuickPaintedItem(aParent),
iTaskQueue(BooksTaskQueue::instance()), iTaskQueue(BooksTaskQueue::scaleQueue()),
iScaleTask(NULL), iScaleTask(NULL),
iBook(NULL), iBook(NULL),
iDefaultImage(NULL), iDefaultImage(NULL),
@ -234,7 +234,6 @@ void BooksCoverWidget::setBook(BooksBook* aBook)
if (aBook) { if (aBook) {
(iBook = aBook)->retain(); (iBook = aBook)->retain();
iBook->requestCoverImage(); iBook->requestCoverImage();
iBookRef = iBook->bookRef();
iCoverImage = iBook->coverImage(); iCoverImage = iBook->coverImage();
iTitle = iBook->title(); iTitle = iBook->title();
connect(iBook, connect(iBook,
@ -246,7 +245,6 @@ void BooksCoverWidget::setBook(BooksBook* aBook)
HDEBUG(iTitle); HDEBUG(iTitle);
} else { } else {
iBook = NULL; iBook = NULL;
iBookRef.reset();
iCoverImage = QImage(); iCoverImage = QImage();
iTitle.clear(); iTitle.clear();
HDEBUG("<none>"); HDEBUG("<none>");
@ -333,7 +331,7 @@ void BooksCoverWidget::onSizeChanged()
bool BooksCoverWidget::empty() const bool BooksCoverWidget::empty() const
{ {
return !iBook || !iBook->hasCoverImage() || iScaledImage.isNull(); return iScaledImage.isNull();
} }
bool BooksCoverWidget::loading() const bool BooksCoverWidget::loading() const
@ -358,6 +356,7 @@ void BooksCoverWidget::scaleImage(bool aWasEmpty)
HWARN("Failed to load" << qPrintable(path)); HWARN("Failed to load" << qPrintable(path));
} }
} }
if (iCoverImage.isNull()) { if (iCoverImage.isNull()) {
if (!iDefaultImage) iDefaultImage = DefaultImage::retain(); if (!iDefaultImage) iDefaultImage = DefaultImage::retain();
if (iDefaultImage) iCoverImage = *iDefaultImage; if (iDefaultImage) iCoverImage = *iDefaultImage;
@ -380,6 +379,8 @@ void BooksCoverWidget::scaleImage(bool aWasEmpty)
iScaledImage = QImage(); iScaledImage = QImage();
} }
updateCenter();
if (aWasEmpty != empty()) { if (aWasEmpty != empty()) {
Q_EMIT emptyChanged(); Q_EMIT emptyChanged();
} }
@ -393,6 +394,7 @@ void BooksCoverWidget::onScaleTaskDone()
iScaleTask->release(this); iScaleTask->release(this);
iScaleTask = NULL; iScaleTask = NULL;
update(); update();
updateCenter();
if (wasEmpty != empty()) { if (wasEmpty != empty()) {
Q_EMIT emptyChanged(); Q_EMIT emptyChanged();
} }
@ -404,6 +406,8 @@ void BooksCoverWidget::paint(QPainter* aPainter)
const qreal h = height(); const qreal h = height();
if (w > 0 && h > 0) { if (w > 0 && h > 0) {
qreal sw, sh; qreal sw, sh;
// This has to be consistent with updateCenter()
if (!iScaledImage.isNull()) { if (!iScaledImage.isNull()) {
sw = iScaledImage.width(); sw = iScaledImage.width();
sh = iScaledImage.height(); sh = iScaledImage.height();
@ -412,7 +416,7 @@ void BooksCoverWidget::paint(QPainter* aPainter)
sh = h; sh = h;
} }
const int x = (w - sw)/2; const int x = floor((w - sw)/2);
const int y = h - sh; const int y = h - sh;
QPainterPath path; QPainterPath path;
@ -424,7 +428,7 @@ void BooksCoverWidget::paint(QPainter* aPainter)
const qreal d = 2*iBorderRadius; const qreal d = 2*iBorderRadius;
w1 = qMin(w, qMax(sw, 2*d)) - iBorderWidth; w1 = qMin(w, qMax(sw, 2*d)) - iBorderWidth;
h1 = qMin(h, qMax(sh, 3*d)) - iBorderWidth; h1 = qMin(h, qMax(sh, 3*d)) - iBorderWidth;
x1 = (w - w1)/2; x1 = floor((w - w1)/2);
y1 = h - h1 - iBorderWidth/2; y1 = h - h1 - iBorderWidth/2;
const qreal x2 = x1 + w1 - d; const qreal x2 = x1 + w1 - d;
@ -439,7 +443,7 @@ void BooksCoverWidget::paint(QPainter* aPainter)
} else { } else {
w1 = sw - iBorderWidth; w1 = sw - iBorderWidth;
h1 = sh - iBorderWidth; h1 = sh - iBorderWidth;
x1 = (w - w1)/2; x1 = floor((w - w1)/2);
y1 = h - h1 - iBorderWidth/2; y1 = h - h1 - iBorderWidth/2;
} }
@ -461,3 +465,28 @@ void BooksCoverWidget::paint(QPainter* aPainter)
} }
} }
} }
void BooksCoverWidget::updateCenter()
{
const QPoint oldCenter(iCenter);
const qreal w = width();
const qreal h = height();
// This has to be consistent with paint()
iCenter.setX(floor(w/2));
if (iScaledImage.isNull()) {
iCenter.setY(floor(h/2));
} else {
iCenter.setY(floor(h - iScaledImage.height()/2));
}
if (iCenter != oldCenter) {
Q_EMIT centerChanged();
if (iCenter.x() != oldCenter.x()) {
Q_EMIT centerXChanged();
}
if (iCenter.y() != oldCenter.y()) {
Q_EMIT centerYChanged();
}
}
}

View file

@ -57,6 +57,9 @@ class BooksCoverWidget: public QQuickPaintedItem
Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor NOTIFY borderColorChanged) Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor NOTIFY borderColorChanged)
Q_PROPERTY(QUrl defaultCover READ defaultCover WRITE setDefaultCover NOTIFY defaultCoverChanged) Q_PROPERTY(QUrl defaultCover READ defaultCover WRITE setDefaultCover NOTIFY defaultCoverChanged)
Q_PROPERTY(BooksBook* book READ book WRITE setBook NOTIFY bookChanged) Q_PROPERTY(BooksBook* book READ book WRITE setBook NOTIFY bookChanged)
Q_PROPERTY(qreal centerX READ centerX NOTIFY centerXChanged)
Q_PROPERTY(qreal centerY READ centerY NOTIFY centerYChanged)
Q_PROPERTY(QPoint center READ center NOTIFY centerChanged)
public: public:
BooksCoverWidget(QQuickItem* aParent = NULL); BooksCoverWidget(QQuickItem* aParent = NULL);
@ -86,6 +89,10 @@ public:
bool synchronous() const { return iSynchronous; } bool synchronous() const { return iSynchronous; }
void setSynchronous(bool aValue); void setSynchronous(bool aValue);
qreal centerX() const { return iCenter.x(); }
qreal centerY() const { return iCenter.y(); }
QPoint center() const { return iCenter; }
Q_SIGNALS: Q_SIGNALS:
void bookChanged(); void bookChanged();
void emptyChanged(); void emptyChanged();
@ -96,6 +103,9 @@ Q_SIGNALS:
void borderRadiusChanged(); void borderRadiusChanged();
void borderColorChanged(); void borderColorChanged();
void defaultCoverChanged(); void defaultCoverChanged();
void centerXChanged();
void centerYChanged();
void centerChanged();
private Q_SLOTS: private Q_SLOTS:
void onCoverImageChanged(); void onCoverImageChanged();
@ -106,6 +116,7 @@ private:
void paint(QPainter *painter); void paint(QPainter *painter);
void scaleImage(bool aWasEmpty); void scaleImage(bool aWasEmpty);
void scaleImage() { scaleImage(empty()); } void scaleImage() { scaleImage(empty()); }
void updateCenter();
private: private:
class ScaleTask; class ScaleTask;
@ -114,7 +125,6 @@ private:
ScaleTask* iScaleTask; ScaleTask* iScaleTask;
QImage iScaledImage; QImage iScaledImage;
QImage iCoverImage; QImage iCoverImage;
shared_ptr<Book> iBookRef;
BooksBook* iBook; BooksBook* iBook;
QImage* iDefaultImage; QImage* iDefaultImage;
qreal iBorderWidth; qreal iBorderWidth;
@ -122,6 +132,7 @@ private:
QColor iBorderColor; QColor iBorderColor;
QUrl iDefaultCover; QUrl iDefaultCover;
QString iTitle; QString iTitle;
QPoint iCenter;
bool iStretch; bool iStretch;
bool iSynchronous; bool iSynchronous;
}; };

View file

@ -278,7 +278,7 @@ BooksImportModel::BooksImportModel(QObject* aParent) :
iProgress(0), iProgress(0),
iSelectedCount(0), iSelectedCount(0),
iAutoRefresh(false), iAutoRefresh(false),
iTaskQueue(BooksTaskQueue::instance()), iTaskQueue(BooksTaskQueue::defaultQueue()),
iTask(NULL) iTask(NULL)
{ {
iSelectedRole.append(BooksImportRoleSelected); iSelectedRole.append(BooksImportRoleSelected);

View file

@ -35,6 +35,7 @@
#define BOOKS_ITEM_H #define BOOKS_ITEM_H
#include "BooksTypes.h" #include "BooksTypes.h"
#include "BooksStorage.h"
#include <QDir> #include <QDir>
@ -65,7 +66,8 @@ public:
virtual QString path() const = 0; virtual QString path() const = 0;
virtual bool accessible() const = 0; virtual bool accessible() const = 0;
virtual void deleteFiles() = 0; virtual void deleteFiles() = 0;
virtual bool copyTo(QDir aDestDir, CopyOperation* aOperation = NULL) = 0; virtual BooksItem* copyTo(const BooksStorage& aStorage, QString aRelPath,
CopyOperation* aOperation = NULL) = 0;
}; };
#endif // BOOKS_ITEM_H #endif // BOOKS_ITEM_H

View file

@ -158,7 +158,7 @@ void BooksPageWidget::RenderTask::performTask()
BooksPageWidget::BooksPageWidget(QQuickItem* aParent) : BooksPageWidget::BooksPageWidget(QQuickItem* aParent) :
QQuickPaintedItem(aParent), QQuickPaintedItem(aParent),
iTaskQueue(BooksTaskQueue::instance()), iTaskQueue(BooksTaskQueue::defaultQueue()),
iTextStyle(BooksTextStyle::defaults()), iTextStyle(BooksTextStyle::defaults()),
iResizeTimer(new QTimer(this)), iResizeTimer(new QTimer(this)),
iModel(NULL), iModel(NULL),

View file

@ -77,9 +77,12 @@ Q_SIGNALS:
public: public:
BooksShelf::Data* iDestData; BooksShelf::Data* iDestData;
BooksStorage iDestStorage;
QString iDestRelPath;
QString iDestAbsPath;
BooksItem* iSrcItem; BooksItem* iSrcItem;
BooksItem* iDestItem;
int iCopyProgress; int iCopyProgress;
bool iSuccess;
}; };
// ========================================================================== // ==========================================================================
@ -323,9 +326,12 @@ inline bool BooksShelf::Data::copyingOut()
BooksShelf::CopyTask::CopyTask(BooksShelf::Data* aDestData, BooksItem* aSrcItem) : BooksShelf::CopyTask::CopyTask(BooksShelf::Data* aDestData, BooksItem* aSrcItem) :
iDestData(aDestData), iDestData(aDestData),
iDestStorage(aDestData->iShelf->storage()),
iDestRelPath(aDestData->iShelf->relativePath()),
iDestAbsPath(iDestStorage.fullPath(iDestRelPath + "/" + aSrcItem->fileName())),
iSrcItem(aSrcItem->retain()), iSrcItem(aSrcItem->retain()),
iCopyProgress(0), iDestItem(NULL),
iSuccess(false) iCopyProgress(0)
{ {
if (iDestData->iCopyTask) { if (iDestData->iCopyTask) {
iDestData->iCopyTask->release(iDestData->iShelf); iDestData->iCopyTask->release(iDestData->iShelf);
@ -341,6 +347,7 @@ BooksShelf::CopyTask::~CopyTask()
{ {
HASSERT(!iDestData); HASSERT(!iDestData);
iSrcItem->release(); iSrcItem->release();
if (iDestItem) iDestItem->release();
} }
inline QString BooksShelf::CopyTask::srcPath() const inline QString BooksShelf::CopyTask::srcPath() const
@ -350,13 +357,12 @@ inline QString BooksShelf::CopyTask::srcPath() const
inline QString BooksShelf::CopyTask::destPath() const inline QString BooksShelf::CopyTask::destPath() const
{ {
return QFileInfo(iDestData->iShelf->path(), return iDestAbsPath;
iSrcItem->fileName()).absoluteFilePath();
} }
void BooksShelf::CopyTask::performTask() void BooksShelf::CopyTask::performTask()
{ {
iSuccess = iSrcItem->copyTo(QDir(iDestData->iShelf->path()), this); iDestItem = iSrcItem->copyTo(iDestStorage, iDestRelPath, this);
} }
bool BooksShelf::CopyTask::isCanceled() const bool BooksShelf::CopyTask::isCanceled() const
@ -470,7 +476,7 @@ BooksShelf::BooksShelf(QObject* aParent) :
iEditMode(false), iEditMode(false),
iRef(-1), iRef(-1),
iSaveTimer(new BooksSaveTimer(this)), iSaveTimer(new BooksSaveTimer(this)),
iTaskQueue(BooksTaskQueue::instance()) iTaskQueue(BooksTaskQueue::defaultQueue())
{ {
init(); init();
connect(iSaveTimer, SIGNAL(save()), SLOT(saveState())); connect(iSaveTimer, SIGNAL(save()), SLOT(saveState()));
@ -485,7 +491,7 @@ BooksShelf::BooksShelf(BooksStorage aStorage, QString aRelativePath) :
iEditMode(false), iEditMode(false),
iRef(1), iRef(1),
iSaveTimer(NULL), iSaveTimer(NULL),
iTaskQueue(BooksTaskQueue::instance()) iTaskQueue(BooksTaskQueue::defaultQueue())
{ {
init(); init();
// Refcounted BooksShelf objects are managed by C++ code // Refcounted BooksShelf objects are managed by C++ code
@ -1088,7 +1094,7 @@ void BooksShelf::onCopyTaskDone()
if (task) { if (task) {
QString dest = task->destPath(); QString dest = task->destPath();
HDEBUG(qPrintable(task->srcPath()) << "->" << qPrintable(dest) << HDEBUG(qPrintable(task->srcPath()) << "->" << qPrintable(dest) <<
"copy" << (task->iSuccess ? "done" : "FAILED")); "copy" << (task->iDestItem ? "done" : "FAILED"));
Data* data = task->iDestData; Data* data = task->iDestData;
const int row = iList.indexOf(data); const int row = iList.indexOf(data);
@ -1099,15 +1105,15 @@ void BooksShelf::onCopyTaskDone()
HASSERT(src); HASSERT(src);
if (src) { if (src) {
src->retain(); src->retain();
if (task->iSuccess) { if (task->iDestItem) {
shared_ptr<Book> book = BooksUtil::bookFromFile(dest); copy = task->iDestItem->book();
if (!book.isNull()) { if (copy) {
copy = new BooksBook(iStorage, iRelativePath, book); copy->retain();
copy->setLastPos(src->lastPos()); copy->setLastPos(src->lastPos());
copy->setCoverImage(src->coverImage()); copy->setCoverImage(src->coverImage());
copy->requestCoverImage(); copy->requestCoverImage();
} else { } else {
HWARN("can't open copied book" << qPrintable(dest)); HWARN("not a book:" << qPrintable(dest));
} }
} }
} }
@ -1167,10 +1173,11 @@ void BooksShelf::deleteFiles()
} }
} }
bool BooksShelf::copyTo(QDir aDestDir, CopyOperation* aOperation) BooksItem* BooksShelf::copyTo(const BooksStorage& aStorage, QString aRelPath,
CopyOperation* aObserver)
{ {
HWARN("copying folders is not implemented!!"); HWARN("copying folders is not implemented!!");
return false; return NULL;
} }
QHash<int,QByteArray> BooksShelf::roleNames() const QHash<int,QByteArray> BooksShelf::roleNames() const

View file

@ -65,7 +65,7 @@ class BooksShelf: public QAbstractListModel, public BooksItem, public BooksLoadi
Q_PROPERTY(int dummyItemIndex READ dummyItemIndex WRITE setDummyItemIndex NOTIFY dummyItemIndexChanged) Q_PROPERTY(int dummyItemIndex READ dummyItemIndex WRITE setDummyItemIndex NOTIFY dummyItemIndexChanged)
Q_PROPERTY(BooksBook* book READ book CONSTANT) Q_PROPERTY(BooksBook* book READ book CONSTANT)
Q_PROPERTY(BooksShelf* shelf READ shelf CONSTANT) Q_PROPERTY(BooksShelf* shelf READ shelf CONSTANT)
Q_PROPERTY(QObject* storage READ storage CONSTANT) Q_PROPERTY(QObject* storage READ storageObject CONSTANT)
public: public:
explicit BooksShelf(QObject* aParent = NULL); explicit BooksShelf(QObject* aParent = NULL);
@ -89,7 +89,8 @@ public:
QString relativePath() const { return iRelativePath; } QString relativePath() const { return iRelativePath; }
void setRelativePath(QString aPath); void setRelativePath(QString aPath);
BooksBook* bookAt(int aIndex) const; BooksBook* bookAt(int aIndex) const;
QObject* storage() { return &iStorage; } QObject* storageObject() { return &iStorage; }
const BooksStorage& storage() const { return iStorage; }
void setName(QString aName); void setName(QString aName);
bool editMode() const { return iEditMode; } bool editMode() const { return iEditMode; }
@ -120,7 +121,8 @@ public:
virtual QString path() const; virtual QString path() const;
virtual bool accessible() const; virtual bool accessible() const;
virtual void deleteFiles(); virtual void deleteFiles();
virtual bool copyTo(QDir aDestDir, CopyOperation* aOperation); virtual BooksItem* copyTo(const BooksStorage& aStorage, QString aRelPath,
CopyOperation* aObserver);
Q_SIGNALS: Q_SIGNALS:
void loadingChanged(); void loadingChanged();

View file

@ -36,32 +36,61 @@
#include "HarbourDebug.h" #include "HarbourDebug.h"
static weak_ptr<BooksTaskQueue> booksTaskQueueInstance; class BooksTaskQueue::Private {
public:
static weak_ptr<BooksTaskQueue> gDefaultQueue;
static weak_ptr<BooksTaskQueue> gScaleQueue;
shared_ptr<BooksTaskQueue> BooksTaskQueue::instance() static BooksTaskQueue* newDefaultQueue() { return new BooksTaskQueue(1); }
{ static BooksTaskQueue* newScaleQueue() { return new BooksTaskQueue(2); }
shared_ptr<BooksTaskQueue> worker;
if (booksTaskQueueInstance.isNull()) { static void waitForDone(shared_ptr<BooksTaskQueue> aQueue, int aMsecs) {
booksTaskQueueInstance = (worker = new BooksTaskQueue()); if (!aQueue.isNull()) {
} else { aQueue->iPool->waitForDone(aMsecs);
worker = booksTaskQueueInstance; }
} }
return worker;
static void waitForDone(int aMsecs) {
waitForDone(gDefaultQueue, aMsecs);
waitForDone(gScaleQueue, aMsecs);
}
static shared_ptr<BooksTaskQueue> get(weak_ptr<BooksTaskQueue>* aQueue,
BooksTaskQueue* (*aNewFunc)())
{
shared_ptr<BooksTaskQueue> queue;
if (aQueue->isNull()) {
*aQueue = (queue = aNewFunc());
} else {
queue = *aQueue;
}
return queue;
}
};
weak_ptr<BooksTaskQueue> BooksTaskQueue::Private::gDefaultQueue;
weak_ptr<BooksTaskQueue> BooksTaskQueue::Private::gScaleQueue;
shared_ptr<BooksTaskQueue> BooksTaskQueue::defaultQueue()
{
return Private::get(&Private::gDefaultQueue, Private::newDefaultQueue);
} }
BooksTaskQueue::BooksTaskQueue() : shared_ptr<BooksTaskQueue> BooksTaskQueue::scaleQueue()
iPool(new QThreadPool)
{ {
HDEBUG("created"); return Private::get(&Private::gScaleQueue, Private::newScaleQueue);
iPool->setMaxThreadCount(1);
} }
void BooksTaskQueue::waitForDone(int aMsecs) void BooksTaskQueue::waitForDone(int aMsecs)
{ {
shared_ptr<BooksTaskQueue> worker = booksTaskQueueInstance; Private::waitForDone(aMsecs);
if (!worker.isNull()) { }
worker->iPool->waitForDone(aMsecs);
} BooksTaskQueue::BooksTaskQueue(int aMaxThreadCount) :
iPool(new QThreadPool)
{
HDEBUG("created");
iPool->setMaxThreadCount(aMaxThreadCount);
} }
BooksTaskQueue::~BooksTaskQueue() BooksTaskQueue::~BooksTaskQueue()

View file

@ -45,17 +45,20 @@ class BooksTaskQueue
friend class shared_ptr_storage<BooksTaskQueue>; friend class shared_ptr_storage<BooksTaskQueue>;
public: public:
static shared_ptr<BooksTaskQueue> instance(); static shared_ptr<BooksTaskQueue> defaultQueue();
static shared_ptr<BooksTaskQueue> scaleQueue();
static void waitForDone(int aMsecs = -1); static void waitForDone(int aMsecs = -1);
void submit(BooksTask* aTask); void submit(BooksTask* aTask);
void submit(BooksTask* aTask, QObject* aTarget, const char* aSlot); void submit(BooksTask* aTask, QObject* aTarget, const char* aSlot);
private: private:
BooksTaskQueue(); BooksTaskQueue(int aMaxThreadCount);
~BooksTaskQueue(); ~BooksTaskQueue();
private: private:
class Private;
friend class Private;
QThreadPool* iPool; QThreadPool* iPool;
}; };