Merge pull request #1098 from kiwix/united_states_of_books

Declaration of USB (Unified States of Books)
This commit is contained in:
Kelson 2024-05-09 15:28:41 +02:00 committed by GitHub
commit 18456eb9c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 162 additions and 62 deletions

View File

@ -218,26 +218,40 @@ void ContentManager::onCustomContextMenu(const QPoint &point)
QAction menuCancelBook(gt("cancel-download"), this);
QAction menuOpenFolder(gt("open-folder"), this);
if (const auto download = bookNode->getDownloadState()) {
if (download->paused) {
contextMenu.addAction(&menuResumeBook);
} else {
contextMenu.addAction(&menuPauseBook);
}
const auto bookState = getBookState(id);
switch ( bookState ) {
case BookState::DOWNLOAD_PAUSED:
contextMenu.addAction(&menuResumeBook);
contextMenu.addAction(&menuCancelBook);
} else {
try {
break;
case BookState::DOWNLOADING:
contextMenu.addAction(&menuPauseBook);
contextMenu.addAction(&menuCancelBook);
break;
case BookState::AVAILABLE_LOCALLY_AND_HEALTHY:
case BookState::ERROR_MISSING_ZIM_FILE:
case BookState::ERROR_CORRUPTED_ZIM_FILE:
{
const auto book = mp_library->getBookById(id);
auto bookPath = QString::fromStdString(book.getPath());
contextMenu.addAction(&menuOpenBook);
if ( bookState == BookState::AVAILABLE_LOCALLY_AND_HEALTHY ) {
contextMenu.addAction(&menuOpenBook);
}
contextMenu.addAction(&menuDeleteBook);
contextMenu.addAction(&menuOpenFolder);
connect(&menuOpenFolder, &QAction::triggered, [=]() {
openFileLocation(bookPath, mp_view);
});
} catch (...) {
contextMenu.addAction(&menuDownloadBook);
break;
}
case BookState::AVAILABLE_ONLINE:
contextMenu.addAction(&menuDownloadBook);
break;
default: break;
}
connect(&menuDeleteBook, &QAction::triggered, [=]() {
@ -390,6 +404,19 @@ QVariant getBookAttribute(const kiwix::Book& b, const QString& a)
return QVariant();
}
ContentManager::BookState getStateOfLocalBook(const kiwix::Book& book)
{
if ( !book.isPathValid() ) {
return ContentManager::BookState::ERROR_MISSING_ZIM_FILE;
}
// XXX: When a book is detected to be corrupted, information about that
// XXX: has to be recorded somewhere so that we can return
// XXX: ERROR_CORRUPTED_ZIM_FILE here
return ContentManager::BookState::AVAILABLE_LOCALLY_AND_HEALTHY;
}
} // unnamed namespace
ContentManager::BookInfo ContentManager::getBookInfos(QString id, const QStringList &keys)
@ -420,17 +447,40 @@ ContentManager::BookInfo ContentManager::getBookInfos(QString id, const QStringL
return values;
}
ContentManager::BookState ContentManager::getBookState(QString bookId)
{
if ( const auto downloadState = m_downloads.value(bookId) ) {
return downloadState->paused
? BookState::DOWNLOAD_PAUSED
: BookState::DOWNLOADING;
// TODO: a download may be in error state
}
try {
const kiwix::Book& b = mp_library->getBookById(bookId);
return b.getDownloadId().empty()
? getStateOfLocalBook(b)
: BookState::DOWNLOADING;
} catch (...) {}
try {
QMutexLocker locker(&remoteLibraryLocker);
const kiwix::Book& b = mp_remoteLibrary->getBookById(bookId.toStdString());
return !b.getUrl().empty()
? BookState::AVAILABLE_ONLINE
: BookState::METADATA_ONLY;
} catch (...) {}
return BookState::INVALID;
}
void ContentManager::openBookWithIndex(const QModelIndex &index)
{
try {
auto bookNode = static_cast<Node*>(index.internalPointer());
const QString bookId = bookNode->getBookId();
// throws std::out_of_range if the book isn't available in local library
const kiwix::Book& book = mp_library->getBookById(bookId);
if ( !book.getDownloadId().empty() )
return;
auto bookNode = static_cast<Node*>(index.internalPointer());
const QString bookId = bookNode->getBookId();
if ( getBookState(bookId) == BookState::AVAILABLE_LOCALLY_AND_HEALTHY ) {
openBook(bookId);
} catch (std::out_of_range &e) {}
}
}
void ContentManager::openBook(const QString &id)

View File

@ -20,6 +20,43 @@ public: // types
typedef ContentManagerModel::BookInfo BookInfo;
typedef ContentManagerModel::BookInfoList BookInfoList;
enum class BookState
{
// Nothing known about a book with that id
INVALID,
// Only (some) metadata is available for the book, however neither a
// ZIM-file nor a URL is associated with it.
METADATA_ONLY,
// No ZIM file is associated with the book but a URL is provided.
AVAILABLE_ONLINE,
// The book is being downloaded.
DOWNLOADING,
// The book started downloading, but the download is currently paused.
DOWNLOAD_PAUSED,
// The book started downloading but the download was stopped due to
// errors.
DOWNLOAD_ERROR,
// A valid ZIM file path is associated with the book and no evidence
// about any issues with that ZIM file has so far been obtained.
AVAILABLE_LOCALLY_AND_HEALTHY,
// A ZIM file path is associated with the book but no such file seems
// to exist (may be caused by missing read permissions for the directory
// containing the ZIM file).
ERROR_MISSING_ZIM_FILE,
// A ZIM file is associated with the book but it cannot be opened
// due to issues with its content.
ERROR_CORRUPTED_ZIM_FILE
};
public: // functions
explicit ContentManager(Library* library, kiwix::Downloader *downloader, QObject *parent = nullptr);
virtual ~ContentManager();
@ -49,6 +86,7 @@ signals:
public slots:
QStringList getTranslations(const QStringList &keys);
BookInfo getBookInfos(QString id, const QStringList &keys);
BookState getBookState(QString id);
void openBook(const QString& id);
void downloadBook(const QString& id);
void downloadBook(const QString& id, QModelIndex index);

View File

@ -146,17 +146,39 @@ void showDownloadProgress(QPainter *painter, QRect box, const DownloadState& dow
createArc(painter, startAngle, spanAngle, rectangle, pen);
}
void ContentManagerDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
void ContentManagerDelegate::paintButton(QPainter *p, const QRect &r, QString t) const
{
QStyleOptionButton button;
QRect r = option.rect;
int x,y,w,h;
x = r.left();
y = r.top();
w = r.width();
h = r.height();
button.rect = QRect(x,y,w,h);
button.rect = r;
button.state = QStyle::State_Enabled;
button.text = t;
baseButton->style()->drawControl( QStyle::CE_PushButton, &button, p, baseButton.data());
}
void ContentManagerDelegate::paintBookState(QPainter *p, QRect r, const QModelIndex &index) const
{
const auto node = static_cast<RowNode*>(index.internalPointer());
const auto id = node->getBookId();
switch ( KiwixApp::instance()->getContentManager()->getBookState(id) ) {
case ContentManager::BookState::AVAILABLE_LOCALLY_AND_HEALTHY:
return paintButton(p, r, gt("open"));
case ContentManager::BookState::AVAILABLE_ONLINE:
return paintButton(p, r, gt("download"));
case ContentManager::BookState::DOWNLOADING:
case ContentManager::BookState::DOWNLOAD_PAUSED:
case ContentManager::BookState::DOWNLOAD_ERROR:
return showDownloadProgress(p, r, *node->getDownloadState());
default:
return;
}
}
void ContentManagerDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QRect r = option.rect;
if (index.parent().isValid()) {
// additional info
QRect nRect = r;
@ -165,24 +187,9 @@ void ContentManagerDelegate::paint(QPainter *painter, const QStyleOptionViewItem
painter->drawText(nRect, Qt::AlignLeft | Qt::AlignVCenter, index.data(Qt::UserRole+1).toString());
return;
}
auto node = static_cast<RowNode*>(index.internalPointer());
try {
const auto id = node->getBookId();
const auto book = KiwixApp::instance()->getLibrary()->getBookById(id);
if ( book.getDownloadId().empty() ) {
button.text = gt("open");
}
} catch (std::out_of_range& e) {
button.text = gt("download");
}
QStyleOptionViewItem eOpt = option;
if (index.column() == 5) {
if (const auto downloadState = node->getDownloadState()) {
showDownloadProgress(painter, r, *downloadState);
}
else {
baseButton->style()->drawControl( QStyle::CE_PushButton, &button, painter, baseButton.data());
}
paintBookState(painter, option.rect, index);
return;
}
if (index.column() == 0) {
@ -192,7 +199,7 @@ void ContentManagerDelegate::paint(QPainter *painter, const QStyleOptionViewItem
QPixmap pix;
pix.loadFromData(iconData);
QIcon icon(pix);
icon.paint(painter, QRect(x+10, y+10, 30, 50));
icon.paint(painter, QRect(r.left()+10, r.top()+10, 30, 50));
return;
}
if (index.column() == 1) {
@ -249,24 +256,25 @@ void ContentManagerDelegate::handleLastColumnClicked(const QModelIndex& index, Q
int x = r.left();
int w = r.width();
if (const auto downloadState = node->getDownloadState()) {
if (downloadState->paused) {
if (clickX < (x + w/2)) {
KiwixApp::instance()->getContentManager()->cancelBook(id);
} else {
KiwixApp::instance()->getContentManager()->resumeBook(id, index);
}
} else {
KiwixApp::instance()->getContentManager()->pauseBook(id, index);
}
} else {
try {
const auto book = KiwixApp::instance()->getLibrary()->getBookById(id);
KiwixApp::instance()->getContentManager()->openBook(id);
} catch (std::out_of_range& e) {
KiwixApp::instance()->getContentManager()->downloadBook(id, index);
}
}
ContentManager& contentMgr = *KiwixApp::instance()->getContentManager();
switch ( contentMgr.getBookState(id) ) {
case ContentManager::BookState::AVAILABLE_LOCALLY_AND_HEALTHY:
return contentMgr.openBook(id);
case ContentManager::BookState::AVAILABLE_ONLINE:
return contentMgr.downloadBook(id, index);
case ContentManager::BookState::DOWNLOADING:
return contentMgr.pauseBook(id, index);
case ContentManager::BookState::DOWNLOAD_PAUSED:
return clickX < (x + w/2)
? contentMgr.cancelBook(id)
: contentMgr.resumeBook(id, index);
default:
return;
}
}
QSize ContentManagerDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const

View File

@ -15,6 +15,10 @@ public:
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private: // functions
void paintBookState(QPainter *p, QRect r, const QModelIndex &index) const;
void paintButton(QPainter *p, const QRect &r, QString t) const;
private:
QScopedPointer<QPushButton> baseButton;
QByteArray placeholderIcon;