Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 132 additions & 13 deletions src/multizimbutton.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#include <QListWidget>
#include <QMenu>
#include <QWidgetAction>
#include <QButtonGroup>
#include <QRadioButton>
#include <QCheckBox>
#include "kiwixapp.h"
#include "multizimbutton.h"
#include "css_constants.h"
Expand All @@ -11,8 +10,7 @@ QString getElidedText(const QFont& font, int length, const QString& text);

MultiZimButton::MultiZimButton(QWidget *parent) :
QToolButton(parent),
mp_buttonList(new QListWidget),
mp_radioButtonGroup(new QButtonGroup(this))
mp_buttonList(new QListWidget)
{
setMenu(new QMenu(this));
setPopupMode(QToolButton::InstantPopup);
Expand All @@ -23,17 +21,82 @@ MultiZimButton::MultiZimButton(QWidget *parent) :
popupAction->setDefaultWidget(mp_buttonList);
menu()->addAction(popupAction);

connect(mp_buttonList, &QListWidget::currentRowChanged, this, [=](int row){
if (const auto widget = getZimWidget(row))
widget->getRadioButton()->setChecked(true);
connect(mp_buttonList, &QListWidget::itemActivated, this, [=](QListWidgetItem *item) {
if (const auto widget = getZimWidget(item))
widget->toggle();
});
connect(mp_buttonList, &QListWidget::itemClicked, this, [=](QListWidgetItem *item) {
if (const auto widget = getZimWidget(item))
widget->toggle();
});
}

void MultiZimButton::updateDisplay()
{
const auto library = KiwixApp::instance()->getLibrary();
const auto view = KiwixApp::instance()->getTabWidget()->currentWebView();
QListWidgetItem* currentItem = nullptr;
QIcon currentIcon;
const int paddingTopBot = CSS::MultiZimButton::QListWidget::paddingVertical * 2;
const int itemHeight = paddingTopBot + CSS::ZimItemWidget::QLabel::lineHeight;

for (int row = 0; row < mp_buttonList->count(); row++)
{
const auto item = mp_buttonList->item(row);
const auto bookId = item->data(Qt::UserRole).toString();
const QString bookTitle = QString::fromStdString(library->getBookById(bookId).getTitle());
const QIcon zimIcon = library->getBookIcon(bookId);
item->setData(Qt::DisplayRole, bookTitle);
item->setSizeHint(QSize(0, itemHeight));

if (view && view->zimId() == bookId)
{
currentItem = item;
currentIcon = zimIcon;
continue;
}

setItemZimWidget(item, bookTitle, zimIcon);
}

mp_buttonList->sortItems();
if (currentItem)
{
const auto checked = getZimWidget(currentItem)->isChecked();
const auto title = currentItem->data(Qt::DisplayRole).toString();
const auto currentRow = mp_buttonList->row(currentItem);
if (currentRow > 0) {
currentItem = mp_buttonList->takeItem(currentRow);
mp_buttonList->insertItem(0, currentItem);
currentItem = mp_buttonList->item(0);
setItemZimWidget(currentItem, "*" + title, currentIcon);
if (checked)
getZimWidget(currentItem)->setChecked(checked);
}
else {
setItemZimWidget(currentItem, "*" + title, currentIcon);
}
}

/* Display should not be used other than for sorting. */
for (int i = 0; i < mp_buttonList->count(); i++)
mp_buttonList->item(i)->setData(Qt::DisplayRole, QVariant());

setDisabled(mp_buttonList->model()->rowCount() == 0);

mp_buttonList->scrollToTop();
mp_buttonList->setCurrentRow(0);

/* We set a maximum display height for list. Respect padding. */
const int listHeight = itemHeight * std::min(7, mp_buttonList->count());
mp_buttonList->setFixedHeight(listHeight + paddingTopBot);
mp_buttonList->setFixedWidth(menu()->width());
}

void MultiZimButton::updateBooks()
{
auto checkedZimIds = getCheckedZimIds();
mp_buttonList->clear();
for (const auto& button : mp_radioButtonGroup->buttons())
mp_radioButtonGroup->removeButton(button);

const auto library = KiwixApp::instance()->getLibrary();
const auto view = KiwixApp::instance()->getTabWidget()->currentWebView();
Expand Down Expand Up @@ -65,6 +128,10 @@ void MultiZimButton::updateDisplay()

mp_buttonList->addItem(item);
setItemZimWidget(item, bookTitle, zimIcon);
if (checkedZimIds.contains(bookId)) {
checkedZimIds.removeOne(bookId);
getZimWidget(item)->setChecked(true);
}
}

mp_buttonList->sortItems();
Expand All @@ -77,6 +144,7 @@ void MultiZimButton::updateDisplay()
}

/* Display should not be used other than for sorting. */

for (int i = 0; i < mp_buttonList->count(); i++)
mp_buttonList->item(i)->setData(Qt::DisplayRole, QVariant());

Expand All @@ -86,6 +154,7 @@ void MultiZimButton::updateDisplay()
mp_buttonList->setCurrentRow(0);

/* We set a maximum display height for list. Respect padding. */

const int listHeight = itemHeight * std::min(7, mp_buttonList->count());
mp_buttonList->setFixedHeight(listHeight + paddingTopBot);
mp_buttonList->setFixedWidth(menu()->width());
Expand All @@ -94,10 +163,30 @@ void MultiZimButton::updateDisplay()
QStringList MultiZimButton::getZimIds() const
{
QStringList idList;
bool someChecked = false;
for (int row = 0; row < mp_buttonList->count(); row++)
{
const auto widget = getZimWidget(row);
if (widget && widget->getRadioButton()->isChecked())
if (widget && widget->isChecked()) {
if (!someChecked) {
someChecked = true;
idList.clear();
}
idList.append(mp_buttonList->item(row)->data(Qt::UserRole).toString());
}
else if (!someChecked)
idList.append(mp_buttonList->item(row)->data(Qt::UserRole).toString());
}
return idList;
}

QStringList MultiZimButton::getCheckedZimIds() const
{
QStringList idList;
for (int row = 0; row < mp_buttonList->count(); row++)
{
const auto widget = getZimWidget(row);
if (widget && widget->isChecked())
idList.append(mp_buttonList->item(row)->data(Qt::UserRole).toString());
}
return idList;
Expand All @@ -109,19 +198,30 @@ ZimItemWidget *MultiZimButton::getZimWidget(int row) const
return qobject_cast<ZimItemWidget *>(widget);
}

ZimItemWidget *MultiZimButton::getZimWidget(QListWidgetItem *item) const
{
const auto widget = mp_buttonList->itemWidget(item);
return qobject_cast<ZimItemWidget *>(widget);
}

void MultiZimButton::setItemZimWidget(QListWidgetItem *item,
const QString &title, const QIcon &icon)
{
const auto oldWidget = getZimWidget(item);
const auto checked = oldWidget
? oldWidget->isChecked()
: false;
const auto zimWidget = new ZimItemWidget(title, icon);
mp_radioButtonGroup->addButton(zimWidget->getRadioButton());
mp_buttonList->setItemWidget(item, zimWidget);
if (checked)
zimWidget->setChecked(true);
}

ZimItemWidget::ZimItemWidget(QString text, QIcon icon, QWidget *parent) :
QWidget(parent),
textLabel(new QLabel(this)),
iconLabel(new QLabel(this)),
radioBt(new QRadioButton(this))
checkBx(new QCheckBox(this))
{
setLayout(new QHBoxLayout);

Expand Down Expand Up @@ -163,5 +263,24 @@ ZimItemWidget::ZimItemWidget(QString text, QIcon icon, QWidget *parent) :

layout()->addWidget(iconLabel);
layout()->addWidget(textLabel);
layout()->addWidget(radioBt);
layout()->addWidget(checkBx);
}

bool ZimItemWidget::isChecked() const {
const auto cb = getCheckBox();
if (cb)
return cb->isChecked();
return false;
}

void ZimItemWidget::setChecked(bool checked) {
const auto cb = getCheckBox();
if (cb)
cb->setChecked(checked);
}

void ZimItemWidget::toggle() {
const auto cb = getCheckBox();
if (cb)
cb->toggle();
}
15 changes: 11 additions & 4 deletions src/multizimbutton.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
class QListWidget;
class QButtonGroup;
class QListWidgetItem;
class QRadioButton;
class QCheckBox;
class QLabel;

class ZimItemWidget : public QWidget {
Expand All @@ -15,12 +15,17 @@ class ZimItemWidget : public QWidget {
public:
ZimItemWidget(QString text, QIcon icon, QWidget *parent = nullptr);

QRadioButton* getRadioButton() const { return radioBt; }
QCheckBox* getCheckBox() const { return checkBx; }
bool isChecked() const;

public slots:
void setChecked(bool checked);
void toggle();

private:
QLabel* textLabel;
QLabel* iconLabel;
QRadioButton* radioBt;
QCheckBox* checkBx;
};

class MultiZimButton : public QToolButton {
Expand All @@ -31,13 +36,15 @@ class MultiZimButton : public QToolButton {

public slots:
void updateDisplay();
void updateBooks();
QStringList getZimIds() const;
QStringList getCheckedZimIds() const;

private:
QListWidget* mp_buttonList;
QButtonGroup* mp_radioButtonGroup;

ZimItemWidget* getZimWidget(int row) const;
ZimItemWidget* getZimWidget(QListWidgetItem *item) const;
void setItemZimWidget(QListWidgetItem* item, const QString& title, const QIcon& icon);
};

Expand Down
2 changes: 1 addition & 1 deletion src/searchbar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ SearchBar::SearchBar(QWidget *parent) :
&BookmarkButton::update_display);
connect(KiwixApp::instance()->getContentManager(),
&ContentManager::booksChanged, &m_multiZimButton,
&MultiZimButton::updateDisplay);
&MultiZimButton::updateBooks);
connect(this, &SearchBar::currentTitleChanged, &m_multiZimButton,
&MultiZimButton::updateDisplay);
}
69 changes: 35 additions & 34 deletions src/suggestionlistworker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,43 @@ void SuggestionListWorker::run()
const auto selectedIdList = app->getSearchBar().getMultiZimButton().getZimIds();

/* TODO: re-implement this after introducing the actual Multi-Zim. */
const auto currentZimId = selectedIdList[0];
try {
const auto archive = app->getLibrary()->getArchive(currentZimId);
QUrl url;
url.setScheme("zim");
url.setHost(currentZimId + ".zim");
auto prefix = m_text.toStdString();
auto suggestionSearcher = zim::SuggestionSearcher(*archive);
auto suggestionSearch = suggestionSearcher.suggest(prefix);
const auto suggestions = suggestionSearch.getResults(m_start, getFetchSize());
for (auto current : suggestions) {
QString path = QString("/") + QString::fromStdString(current.getPath());
url.setPath(path);
const auto text = QString::fromStdString(current.getTitle());
suggestionList.append({text, url});
}
for (const auto &currentZimId : selectedIdList) {
try {
const auto archive = app->getLibrary()->getArchive(currentZimId);
QUrl url;
url.setScheme("zim");
url.setHost(currentZimId + ".zim");
auto prefix = m_text.toStdString();
auto suggestionSearcher = zim::SuggestionSearcher(*archive);
auto suggestionSearch = suggestionSearcher.suggest(prefix);
const auto suggestions = suggestionSearch.getResults(m_start, getFetchSize());
for (auto current : suggestions) {
QString path = QString("/") + QString::fromStdString(current.getPath());
url.setPath(path);
const auto text = QString::fromStdString(current.getTitle());
suggestionList.append({text, url});
}

// Propose fulltext search
url.setPath("");
if (archive->hasFulltextIndex()) {
// The host is used to determine the currentZimId
// The content query item is used to know in which zim search (as for kiwix-serve)
url.setHost(currentZimId + ".search");
QUrlQuery query;
query.addQueryItem("content", currentZimId);
query.addQueryItem("pattern", m_text);
url.setQuery(query);
const auto text = m_text + " (" + gt("fulltext-search") + ")";
suggestionList.append({text, url});
// Propose fulltext search
url.setPath("");
if (archive->hasFulltextIndex()) {
// The host is used to determine the currentZimId
// The content query item is used to know in which zim search (as for kiwix-serve)
url.setHost(currentZimId + ".search");
QUrlQuery query;
query.addQueryItem("content", currentZimId);
query.addQueryItem("pattern", m_text);
url.setQuery(query);
const auto text = m_text + " (" + gt("fulltext-search") + ")";
suggestionList.append({text, url});
}
} catch (std::out_of_range& e) {
// Impossible to find the requested archive (bug ?)
// We could propose a suggestion to do multi-zim search with:
// url.setHost("library.search");
// but we don't have a correct UI to select on which zim search, how to display results, ...
// So do nothing for now
}
} catch (std::out_of_range& e) {
// Impossible to find the requested archive (bug ?)
// We could propose a suggestion to do multi-zim search with:
// url.setHost("library.search");
// but we don't have a correct UI to select on which zim search, how to display results, ...
// So do nothing for now
}
emit(searchFinished(suggestionList, m_token));
}