mirror of
https://github.com/kiwix/kiwix-desktop.git
synced 2025-09-23 03:58:56 -04:00
Icon downloader util for all files view
Added an icon downloader to display icons for remote library. While the icons are being downloaded, a placeholder icon is shown.
This commit is contained in:
parent
ef0227b08c
commit
ff5f022cd6
@ -40,6 +40,7 @@ SOURCES += \
|
||||
src/findinpagebar.cpp \
|
||||
src/node.cpp \
|
||||
src/suggestionlistworker.cpp \
|
||||
src/thumbnaildownloader.cpp \
|
||||
src/translation.cpp \
|
||||
src/main.cpp \
|
||||
src/mainwindow.cpp \
|
||||
@ -77,6 +78,7 @@ HEADERS += \
|
||||
src/findinpagebar.h \
|
||||
src/node.h \
|
||||
src/suggestionlistworker.h \
|
||||
src/thumbnaildownloader.h \
|
||||
src/translation.h \
|
||||
src/mainwindow.h \
|
||||
src/kiwixapp.h \
|
||||
|
@ -27,7 +27,7 @@ ContentManager::ContentManager(Library* library, kiwix::Downloader* downloader,
|
||||
// mp_view will be passed to the tab who will take ownership,
|
||||
// so, we don't need to delete it.
|
||||
mp_view = new ContentManagerView();
|
||||
auto managerModel = new ContentManagerModel();
|
||||
managerModel = new ContentManagerModel(this);
|
||||
const auto booksList = getBooksList();
|
||||
managerModel->setBooksData(booksList);
|
||||
auto treeView = mp_view->getView();
|
||||
@ -54,6 +54,7 @@ ContentManager::ContentManager(Library* library, kiwix::Downloader* downloader,
|
||||
connect(this, &ContentManager::booksChanged, this, [=]() {
|
||||
const auto nBookList = getBooksList();
|
||||
managerModel->setBooksData(nBookList);
|
||||
managerModel->refreshIcons();
|
||||
});
|
||||
connect(&m_remoteLibraryManager, &OpdsRequestManager::requestReceived, this, &ContentManager::updateRemoteLibrary);
|
||||
connect(mp_view->getView(), SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onCustomContextMenu(const QPoint &)));
|
||||
@ -63,27 +64,10 @@ QList<QMap<QString, QVariant>> ContentManager::getBooksList()
|
||||
{
|
||||
const auto bookIds = getBookIds();
|
||||
QList<QMap<QString, QVariant>> bookList;
|
||||
QStringList keys = {"title", "tags", "date", "id", "size", "description"};
|
||||
auto app = KiwixApp::instance();
|
||||
std::shared_ptr<zim::Archive> archive;
|
||||
QStringList keys = {"title", "tags", "date", "id", "size", "description", "faviconUrl"};
|
||||
QIcon bookIcon;
|
||||
for (auto bookId : bookIds) {
|
||||
try {
|
||||
archive = app->getLibrary()->getArchive(bookId);
|
||||
std::string favicon, _mimetype;
|
||||
auto item = archive->getIllustrationItem(48);
|
||||
favicon = item.getData();
|
||||
_mimetype = item.getMimetype();
|
||||
QPixmap pixmap;
|
||||
pixmap.loadFromData((const uchar*)favicon.data(), favicon.size());
|
||||
bookIcon = QIcon(pixmap);
|
||||
} catch (zim::EntryNotFound &e) {
|
||||
bookIcon = QIcon(":/icons/placeholder-icon.png");
|
||||
} catch (std::out_of_range &e) {
|
||||
bookIcon = QIcon(":/icons/placeholder-icon.png");
|
||||
}
|
||||
auto mp = getBookInfos(bookId, keys);
|
||||
mp["icon"] = bookIcon;
|
||||
bookList.append(mp);
|
||||
}
|
||||
return bookList;
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <kiwix/downloader.h>
|
||||
#include "opdsrequestmanager.h"
|
||||
#include "contenttypefilter.h"
|
||||
#include "contentmanagermodel.h"
|
||||
|
||||
class ContentManager : public QObject
|
||||
{
|
||||
@ -26,6 +27,7 @@ public:
|
||||
void setCurrentLanguage(QString language);
|
||||
void setCurrentCategoryFilter(QString category);
|
||||
void setCurrentContentTypeFilter(QList<ContentTypeFilter*>& contentTypeFilter);
|
||||
bool isLocal() const { return m_local; }
|
||||
|
||||
private:
|
||||
Library* mp_library;
|
||||
@ -44,6 +46,7 @@ private:
|
||||
QStringList getBookIds();
|
||||
void eraseBookFilesFromComputer(const QString dirPath, const QString filename);
|
||||
QList<QMap<QString, QVariant>> getBooksList();
|
||||
ContentManagerModel *managerModel;
|
||||
|
||||
signals:
|
||||
void filterParamsChanged();
|
||||
|
@ -16,6 +16,10 @@ ContentManagerDelegate::ContentManagerDelegate(QObject *parent)
|
||||
"font-family: Selawik;"
|
||||
"color: blue;"
|
||||
"margin: 4px;");
|
||||
QImage placeholderIconFile(":/icons/placeholder-icon.png");
|
||||
QBuffer buffer(&placeholderIcon);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
placeholderIconFile.save(&buffer, "png");
|
||||
}
|
||||
|
||||
|
||||
@ -54,7 +58,12 @@ void ContentManagerDelegate::paint(QPainter *painter, const QStyleOptionViewItem
|
||||
return;
|
||||
}
|
||||
if (index.column() == 0) {
|
||||
const auto icon = index.data().value<QIcon>();
|
||||
auto iconData = index.data().value<QByteArray>();
|
||||
if (iconData.isNull())
|
||||
iconData = placeholderIcon;
|
||||
QPixmap pix;
|
||||
pix.loadFromData(iconData);
|
||||
QIcon icon(pix);
|
||||
icon.paint(painter, QRect(x+10, y+10, 30, 50));
|
||||
return;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QPushButton>
|
||||
#include <QByteArray>
|
||||
|
||||
class ContentManagerDelegate : public QStyledItemDelegate
|
||||
{
|
||||
@ -16,6 +17,7 @@ public:
|
||||
|
||||
private:
|
||||
QScopedPointer<QPushButton> baseButton;
|
||||
QByteArray placeholderIcon;
|
||||
};
|
||||
|
||||
#endif // CONTENTMANAGERDELEGATE_H
|
||||
|
@ -5,12 +5,15 @@
|
||||
#include<QDebug>
|
||||
#include <QStringList>
|
||||
#include <QSize>
|
||||
#include <QIcon>
|
||||
#include <zim/error.h>
|
||||
#include <zim/item.h>
|
||||
#include "kiwixapp.h"
|
||||
|
||||
|
||||
ContentManagerModel::ContentManagerModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
{
|
||||
connect(&td, &ThumbnailDownloader::oneThumbnailDownloaded, this, &ContentManagerModel::updateImage);
|
||||
}
|
||||
|
||||
ContentManagerModel::~ContentManagerModel()
|
||||
@ -126,6 +129,7 @@ QString convertToUnits(QString size)
|
||||
|
||||
void ContentManagerModel::setupNodes()
|
||||
{
|
||||
QByteArray bookIcon;
|
||||
beginResetModel();
|
||||
for (auto bookItem : m_data) {
|
||||
auto name = bookItem["title"].toString();
|
||||
@ -134,8 +138,20 @@ void ContentManagerModel::setupNodes()
|
||||
auto content = bookItem["tags"].toString();
|
||||
auto id = bookItem["id"].toString();
|
||||
auto description = bookItem["description"].toString();
|
||||
auto icon = bookItem["icon"];
|
||||
const auto temp = new Node({icon, name, date, size, content, id}, rootNode, id);
|
||||
auto faviconUrl = "https://" + bookItem["faviconUrl"].toString();
|
||||
try {
|
||||
auto book = KiwixApp::instance()->getLibrary()->getBookById(id);
|
||||
std::string favicon;
|
||||
auto item = book.getIllustration(48);
|
||||
favicon = item->getData();
|
||||
bookIcon = QByteArray::fromRawData(reinterpret_cast<const char*>(favicon.data()), favicon.size());
|
||||
bookIcon.detach(); // deep copy
|
||||
} catch (std::out_of_range &e) {
|
||||
if (iconMap.contains(faviconUrl)) {
|
||||
bookIcon = iconMap[faviconUrl];
|
||||
}
|
||||
}
|
||||
const auto temp = new Node({bookIcon, name, date, size, content, id}, rootNode, id);
|
||||
const auto tempsTemp = new Node({"", description, "", "", "", ""}, temp, "", true);
|
||||
temp->appendChild(tempsTemp);
|
||||
rootNode->appendChild(temp);
|
||||
@ -143,6 +159,27 @@ void ContentManagerModel::setupNodes()
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void ContentManagerModel::refreshIcons()
|
||||
{
|
||||
if (KiwixApp::instance()->getContentManager()->isLocal())
|
||||
return;
|
||||
td.clearQueue();
|
||||
for (auto i = 0; i < rowCount() && i < m_data.size(); i++) {
|
||||
auto bookItem = m_data[i];
|
||||
auto id = bookItem["id"].toString();
|
||||
auto faviconUrl = "https://" + bookItem["faviconUrl"].toString();
|
||||
auto app = KiwixApp::instance();
|
||||
try {
|
||||
auto book = app->getLibrary()->getBookById(id);
|
||||
auto item = book.getIllustration(48);
|
||||
} catch (std::out_of_range &e) {
|
||||
if (faviconUrl != "" && !iconMap.contains(faviconUrl)) {
|
||||
td.addDownload(faviconUrl, index(i, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ContentManagerModel::hasChildren(const QModelIndex &parent) const
|
||||
{
|
||||
Node *item = static_cast<Node*>(parent.internalPointer());
|
||||
@ -167,6 +204,7 @@ void ContentManagerModel::fetchMore(const QModelIndex &parent)
|
||||
beginInsertRows(QModelIndex(), zimCount, zimCount + zimsToFetch - 1);
|
||||
zimCount += zimsToFetch;
|
||||
endInsertRows();
|
||||
refreshIcons();
|
||||
}
|
||||
|
||||
void ContentManagerModel::sort(int column, Qt::SortOrder order)
|
||||
@ -190,3 +228,11 @@ void ContentManagerModel::sort(int column, Qt::SortOrder order)
|
||||
}
|
||||
KiwixApp::instance()->getContentManager()->setSortBy(sortBy, order == Qt::AscendingOrder);
|
||||
}
|
||||
|
||||
void ContentManagerModel::updateImage(QModelIndex index, QString url, QByteArray imageData)
|
||||
{
|
||||
Node *item = static_cast<Node*>(index.internalPointer());
|
||||
item->setIconData(imageData);
|
||||
iconMap[url] = imageData;
|
||||
emit dataChanged(index, index);
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <QAbstractItemModel>
|
||||
#include <QModelIndex>
|
||||
#include <QVariant>
|
||||
#include <QIcon>
|
||||
#include "thumbnaildownloader.h"
|
||||
|
||||
class Node;
|
||||
|
||||
@ -28,6 +30,10 @@ public:
|
||||
void setupNodes();
|
||||
bool hasChildren(const QModelIndex &parent) const override;
|
||||
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
|
||||
void refreshIcons();
|
||||
|
||||
public slots:
|
||||
void updateImage(QModelIndex index, QString url, QByteArray imageData);
|
||||
|
||||
protected:
|
||||
bool canFetchMore(const QModelIndex &parent) const override;
|
||||
@ -37,6 +43,8 @@ private:
|
||||
QList<QMap<QString, QVariant>> m_data;
|
||||
Node *rootNode;
|
||||
int zimCount = 0;
|
||||
ThumbnailDownloader td;
|
||||
QMap<QString, QByteArray> iconMap;
|
||||
};
|
||||
|
||||
#endif // CONTENTMANAGERMODEL_H
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <QVariant>
|
||||
#include <QList>
|
||||
#include "contentmanagermodel.h"
|
||||
#include <QIcon>
|
||||
|
||||
class Node
|
||||
{
|
||||
@ -19,6 +20,7 @@ public:
|
||||
Node *parentItem();
|
||||
bool isAdditonal() const { return m_isAdditonal; }
|
||||
QString getBookId() const { return m_bookId; }
|
||||
void setIconData(QByteArray iconData) { m_itemData[0] = iconData; }
|
||||
|
||||
private:
|
||||
QList<QVariant> m_itemData;
|
||||
|
92
src/rownode.cpp
Normal file
92
src/rownode.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
#include "rownode.h"
|
||||
#include <QVariant>
|
||||
#include "kiwixapp.h"
|
||||
#include "descriptionnode.h"
|
||||
#include "kiwix/tools.h"
|
||||
|
||||
RowNode::RowNode(QList<QVariant> itemData, QString bookId, std::weak_ptr<RowNode> parent)
|
||||
: m_itemData(itemData), m_parentItem(parent), m_bookId(bookId)
|
||||
{
|
||||
m_downloadInfo = {0, "", "", false};
|
||||
}
|
||||
|
||||
RowNode::~RowNode()
|
||||
{}
|
||||
|
||||
void RowNode::appendChild(std::shared_ptr<Node> item)
|
||||
{
|
||||
m_childItems.append(item);
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> RowNode::child(int row)
|
||||
{
|
||||
if (row < 0 || row >= m_childItems.size())
|
||||
return nullptr;
|
||||
return m_childItems.at(row);
|
||||
}
|
||||
|
||||
int RowNode::childCount() const
|
||||
{
|
||||
return m_childItems.count();
|
||||
}
|
||||
|
||||
int RowNode::columnCount() const
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> RowNode::parentItem()
|
||||
{
|
||||
std::shared_ptr<Node> temp = m_parentItem.lock();
|
||||
return temp;
|
||||
}
|
||||
|
||||
QVariant RowNode::data(int column)
|
||||
{
|
||||
if (column < 0 || column >= m_itemData.size())
|
||||
return QVariant();
|
||||
return m_itemData.at(column);
|
||||
}
|
||||
|
||||
int RowNode::row() const
|
||||
{
|
||||
try {
|
||||
std::shared_ptr<RowNode> temp = m_parentItem.lock();
|
||||
return temp->m_childItems.indexOf(std::const_pointer_cast<RowNode>(shared_from_this()));
|
||||
} catch(...) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<RowNode> RowNode::createNode(QMap<QString, QVariant> bookItem, QMap<QString, QByteArray> iconMap, std::shared_ptr<RowNode> rootNode)
|
||||
{
|
||||
auto faviconUrl = "https://" + bookItem["faviconUrl"].toString();
|
||||
QString id = bookItem["id"].toString();
|
||||
QByteArray bookIcon;
|
||||
try {
|
||||
auto book = KiwixApp::instance()->getLibrary()->getBookById(id);
|
||||
std::string favicon;
|
||||
auto item = book.getIllustration(48);
|
||||
favicon = item->getData();
|
||||
bookIcon = QByteArray::fromRawData(reinterpret_cast<const char*>(favicon.data()), favicon.size());
|
||||
bookIcon.detach(); // deep copy
|
||||
} catch (...) {
|
||||
bookIcon = QByteArray();
|
||||
if (iconMap.contains(faviconUrl)) {
|
||||
bookIcon = iconMap[faviconUrl];
|
||||
}
|
||||
}
|
||||
std::weak_ptr<RowNode> weakRoot = rootNode;
|
||||
auto rowNodePtr = std::make_shared<RowNode>(
|
||||
RowNode({bookIcon, bookItem["title"],
|
||||
bookItem["date"],
|
||||
QString::fromStdString(kiwix::beautifyFileSize(bookItem["size"].toULongLong())),
|
||||
bookItem["tags"]
|
||||
}, id, weakRoot));
|
||||
std::weak_ptr<RowNode> weakRowNodePtr = rowNodePtr;
|
||||
const auto descNodePtr = std::make_shared<DescriptionNode>(DescriptionNode(bookItem["description"].toString(), weakRowNodePtr));
|
||||
rowNodePtr->appendChild(descNodePtr);
|
||||
return rowNodePtr;
|
||||
}
|
52
src/thumbnaildownloader.cpp
Normal file
52
src/thumbnaildownloader.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "thumbnaildownloader.h"
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QPixmap>
|
||||
#include <QIcon>
|
||||
|
||||
ThumbnailDownloader::ThumbnailDownloader(QObject *parent)
|
||||
{
|
||||
connect(this, &ThumbnailDownloader::oneThumbnailDownloaded, [=]() {
|
||||
if (m_urlPairList.size() != 0)
|
||||
downloadOnePair(m_urlPairList.takeFirst());
|
||||
else
|
||||
m_isDownloading = false;
|
||||
});
|
||||
}
|
||||
|
||||
ThumbnailDownloader::~ThumbnailDownloader()
|
||||
{
|
||||
}
|
||||
|
||||
void ThumbnailDownloader::addDownload(QString url, QModelIndex index)
|
||||
{
|
||||
m_urlPairList.append({index, url});
|
||||
if (!m_isDownloading)
|
||||
startDownload();
|
||||
}
|
||||
|
||||
void ThumbnailDownloader::startDownload()
|
||||
{
|
||||
if (m_urlPairList.size() == 0) {
|
||||
m_isDownloading = false;
|
||||
return;
|
||||
}
|
||||
m_isDownloading = true;
|
||||
downloadOnePair(m_urlPairList.takeFirst());
|
||||
}
|
||||
|
||||
void ThumbnailDownloader::downloadOnePair(QPair<QModelIndex, QString> urlPair)
|
||||
{
|
||||
QNetworkRequest req(urlPair.second);
|
||||
auto reply = manager.get(req);
|
||||
connect(reply, &QNetworkReply::finished, this, [=](){
|
||||
fileDownloaded(reply, urlPair);
|
||||
});
|
||||
}
|
||||
|
||||
void ThumbnailDownloader::fileDownloaded(QNetworkReply *pReply, QPair<QModelIndex, QString> urlPair)
|
||||
{
|
||||
auto downloadedData = pReply->readAll();
|
||||
emit oneThumbnailDownloaded(urlPair.first, urlPair.second, downloadedData);
|
||||
pReply->deleteLater();
|
||||
}
|
37
src/thumbnaildownloader.h
Normal file
37
src/thumbnaildownloader.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef THUMBNAILDOWNLOADER_H
|
||||
#define THUMBNAILDOWNLOADER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QQueue>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QIcon>
|
||||
#include <QNetworkReply>
|
||||
#include <QModelIndex>
|
||||
|
||||
class ThumbnailDownloader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ThumbnailDownloader(QObject *parent = 0);
|
||||
~ThumbnailDownloader();
|
||||
|
||||
void addDownload(QString url, QModelIndex index);
|
||||
void startDownload();
|
||||
void downloadOnePair(QPair<QModelIndex, QString> urlPair);
|
||||
void clearQueue() { m_urlPairList.clear(); }
|
||||
|
||||
signals:
|
||||
void oneThumbnailDownloaded(QModelIndex, QString, QByteArray);
|
||||
|
||||
private:
|
||||
QQueue<QPair<QModelIndex, QString>> m_urlPairList;
|
||||
QNetworkAccessManager manager;
|
||||
bool m_isDownloading = false;
|
||||
|
||||
private slots:
|
||||
void fileDownloaded(QNetworkReply *pReply, QPair<QModelIndex, QString> urlPair);
|
||||
|
||||
};
|
||||
|
||||
#endif // THUMBNAILDOWNLOADER_H
|
Loading…
x
Reference in New Issue
Block a user