Skip to content

Commit

Permalink
Store search history
Browse files Browse the repository at this point in the history
  • Loading branch information
glassez committed Jan 26, 2025
1 parent 3978137 commit 7221199
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 4 deletions.
15 changes: 15 additions & 0 deletions src/base/preferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,21 @@ void Preferences::setSearchEnabled(const bool enabled)
setValue(u"Preferences/Search/SearchEnabled"_s, enabled);
}

int Preferences::searchHistoryLength() const
{
const int val = value(u"Search/HistoryLength"_s, 50);
return std::clamp(val, 0, 99);
}

void Preferences::setSearchHistoryLength(const int length)
{
const int clampedLength = std::clamp(length, 0, 99);
if (clampedLength == searchHistoryLength())
return;

setValue(u"Search/HistoryLength"_s, clampedLength);
}

bool Preferences::storeOpenedSearchTabs() const
{
return value(u"Search/StoreOpenedSearchTabs"_s, false);
Expand Down
2 changes: 2 additions & 0 deletions src/base/preferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ class Preferences final : public QObject
void setSearchEnabled(bool enabled);

// Search UI
int searchHistoryLength() const;
void setSearchHistoryLength(int length);
bool storeOpenedSearchTabs() const;
void setStoreOpenedSearchTabs(bool enabled);
bool storeOpenedSearchTabResults() const;
Expand Down
3 changes: 3 additions & 0 deletions src/gui/optionsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1281,9 +1281,11 @@ void OptionsDialog::loadSearchTabOptions()

m_ui->groupStoreOpenedTabs->setChecked(pref->storeOpenedSearchTabs());
m_ui->checkStoreTabsSearchResults->setChecked(pref->storeOpenedSearchTabResults());
m_ui->searchHistoryLengthSpinBox->setValue(pref->searchHistoryLength());

connect(m_ui->groupStoreOpenedTabs, &QGroupBox::toggled, this, &OptionsDialog::enableApplyButton);
connect(m_ui->checkStoreTabsSearchResults, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton);
connect(m_ui->searchHistoryLengthSpinBox, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton);
}

void OptionsDialog::saveSearchTabOptions() const
Expand All @@ -1292,6 +1294,7 @@ void OptionsDialog::saveSearchTabOptions() const

pref->setStoreOpenedSearchTabs(m_ui->groupStoreOpenedTabs->isChecked());
pref->setStoreOpenedSearchTabResults(m_ui->checkStoreTabsSearchResults->isChecked());
pref->setSearchHistoryLength(m_ui->searchHistoryLengthSpinBox->value());
}

#ifndef DISABLE_WEBUI
Expand Down
37 changes: 37 additions & 0 deletions src/gui/optionsdialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -3272,6 +3272,43 @@ Disable encryption: Only connect to peers without protocol encryption</string>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="searchHistoryLayout">
<item>
<widget class="QLabel" name="searchHistoryLengthLabel">
<property name="text">
<string>History length</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="searchHistoryLengthSpinBox">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::PlusMinus</enum>
</property>
<property name="maximum">
<number>99</number>
</property>
<property name="stepType">
<enum>QAbstractSpinBox::StepType::DefaultStepType</enum>
</property>
</widget>
</item>
<item>
<spacer name="searchHistoryLengthSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
Expand Down
160 changes: 156 additions & 4 deletions src/gui/search/searchwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

#include <utility>

#include <QCompleter>
#include <QDebug>
#include <QEvent>
#include <QJsonArray>
Expand All @@ -48,6 +49,9 @@
#include <QObject>
#include <QRegularExpression>
#include <QShortcut>
#include <QStringList>
#include <QStringListModel>
#include <QTextStream>
#include <QThread>

#include "base/global.h"
Expand All @@ -67,7 +71,12 @@
#include "searchjobwidget.h"
#include "ui_searchwidget.h"

const int HISTORY_FILE_MAX_SIZE = 10 * 1024 * 1024;
const int SESSION_FILE_MAX_SIZE = 10 * 1024 * 1024;
const int RESULTS_FILE_MAX_SIZE = 10 * 1024 * 1024;

const QString DATA_FOLDER_NAME = u"SearchUI"_s;
const QString HISTORY_FILE_NAME = u"History.json"_s;
const QString SESSION_FILE_NAME = u"Session.json"_s;

const QString KEY_SESSION_TABS = u"Tabs"_s;
Expand Down Expand Up @@ -132,10 +141,29 @@ namespace
return tabName;
}

nonstd::expected<QStringList, QString> loadHistory(const Path &filePath, const int limit)
{
const auto readResult = Utils::IO::readFile(filePath, HISTORY_FILE_MAX_SIZE);
if (!readResult)
{
if (readResult.error().status == Utils::IO::ReadError::NotExist)
return {};

return nonstd::make_unexpected(readResult.error().message);
}

QByteArray historyData = readResult.value();
QStringList history;
QTextStream stream {&historyData};
while (!((history.size() >= limit) || stream.atEnd()))
history.append(stream.readLine());

return history;
}

nonstd::expected<SessionData, QString> loadSession(const Path &filePath)
{
const int fileMaxSize = 10 * 1024 * 1024;
const auto readResult = Utils::IO::readFile(filePath, fileMaxSize);
const auto readResult = Utils::IO::readFile(filePath, SESSION_FILE_MAX_SIZE);
if (!readResult)
{
if (readResult.error().status == Utils::IO::ReadError::NotExist)
Expand Down Expand Up @@ -191,8 +219,7 @@ namespace

nonstd::expected<QList<SearchResult>, QString> loadSearchResults(const Path &filePath)
{
const int fileMaxSize = 10 * 1024 * 1024;
const auto readResult = Utils::IO::readFile(filePath, fileMaxSize);
const auto readResult = Utils::IO::readFile(filePath, RESULTS_FILE_MAX_SIZE);
if (!readResult)
{
if (readResult.error().status != Utils::IO::ReadError::NotExist)
Expand Down Expand Up @@ -285,8 +312,12 @@ class SearchWidget::DataStorage final : public QObject
void removeSession();
void storeTab(const QString &tabID, const QList<SearchResult> &searchResults);
void removeTab(const QString &tabID);
void loadHistory(int limit);
void storeHistory(const QStringList &history);
void removeHistory();

signals:
void historyLoaded(const QStringList &history);
void sessionLoaded(const SessionData &sessionData);
void tabLoaded(const QString &tabID, const QString &searchPattern, const QList<SearchResult> &searchResults);
};
Expand Down Expand Up @@ -379,6 +410,7 @@ SearchWidget::SearchWidget(IGUIApplication *app, QWidget *parent)
const auto *focusSearchHotkeyAlternative = new QShortcut((Qt::CTRL | Qt::Key_E), this);
connect(focusSearchHotkeyAlternative, &QShortcut::activated, this, &SearchWidget::toggleFocusBetweenLineEdits);

m_historyLength = Preferences::instance()->searchHistoryLength();
m_storeOpenedTabs = Preferences::instance()->storeOpenedSearchTabs();
m_storeOpenedTabsResults = Preferences::instance()->storeOpenedSearchTabResults();
connect(Preferences::instance(), &Preferences::changed, this, &SearchWidget::onPreferencesChanged);
Expand All @@ -388,6 +420,7 @@ SearchWidget::SearchWidget(IGUIApplication *app, QWidget *parent)
m_ioThread->setObjectName("SearchWidget m_ioThread");
m_ioThread->start();

loadHistory();
restoreSession();
}

Expand Down Expand Up @@ -469,6 +502,30 @@ void SearchWidget::onPreferencesChanged()
}
}
}

const int historyLength = pref->searchHistoryLength();
if (historyLength != m_historyLength)
{
if (historyLength <= 0)
{
QMetaObject::invokeMethod(m_dataStorage, [this] { m_dataStorage->removeHistory(); });
}
else if (historyLength < m_historyLength)
{
QCompleter *completer = m_ui->lineEditSearchPattern->completer();
auto *historyModel = static_cast<QStringListModel *>(completer->model());
QStringList history = historyModel->stringList();
if (history.size() > m_historyLength)
{
history = history.mid(history.size() - m_historyLength);
historyModel->setStringList(history);

QMetaObject::invokeMethod(m_dataStorage, [this, history] { m_dataStorage->storeHistory(history); });
}
}

m_historyLength = historyLength;
}
}

void SearchWidget::fillCatCombobox()
Expand Down Expand Up @@ -552,6 +609,70 @@ int SearchWidget::addTab(const QString &tabID, SearchJobWidget *searchJobWdget)
return m_ui->tabWidget->addTab(searchJobWdget, makeTabName(searchJobWdget));
}

void SearchWidget::updateHistory(const QString &newSearchPattern)
{
if (m_historyLength <= 0)
return;

QStringList history;
QCompleter *completer = m_ui->lineEditSearchPattern->completer();
if (!completer)
{
history.append(newSearchPattern);
completer = new QCompleter(this);
completer->setModel(new QStringListModel(history, this));
m_ui->lineEditSearchPattern->setCompleter(completer);
}
else
{
auto *historyModel = static_cast<QStringListModel *>(completer->model());
history = historyModel->stringList();
if (history.contains(newSearchPattern))
return;

history.append(newSearchPattern);
if (history.size() > m_historyLength)
history = history.mid(1);
historyModel->setStringList(history);
}

QMetaObject::invokeMethod(m_dataStorage, [this, history] { m_dataStorage->storeHistory(history); });
}

void SearchWidget::loadHistory()
{
if (m_historyLength <= 0)
return;

connect(m_dataStorage, &DataStorage::historyLoaded, this, [this](const QStringList &history)
{
QCompleter *completer = m_ui->lineEditSearchPattern->completer();
if (!completer)
{
completer = new QCompleter(this);
completer->setModel(new QStringListModel(history, this));
m_ui->lineEditSearchPattern->setCompleter(completer);
}
else
{
auto *historyModel = static_cast<QStringListModel *>(completer->model());
QStringList newHistory = history;
for (const QString &newPattern : historyModel->stringList())
{
if (!newHistory.contains(newPattern))
newHistory.append(newPattern);
}

if (newHistory.size() > m_historyLength)
newHistory = newHistory.mid(newHistory.size() - m_historyLength);

historyModel->setStringList(newHistory);
}
});

QMetaObject::invokeMethod(m_dataStorage, [this] { m_dataStorage->loadHistory(m_historyLength); });
}

void SearchWidget::saveSession() const
{
if (!m_storeOpenedTabs)
Expand Down Expand Up @@ -750,6 +871,7 @@ void SearchWidget::searchButtonClicked()
m_ui->tabWidget->setTabIcon(tabIndex, UIThemeManager::instance()->getIcon(statusIconName(newTab->status())));
m_ui->tabWidget->setCurrentWidget(newTab);
adjustSearchButton();
updateHistory(pattern);
saveSession();
}

Expand Down Expand Up @@ -918,4 +1040,34 @@ void SearchWidget::DataStorage::removeTab(const QString &tabID)
Utils::Fs::removeFile(makeDataFilePath(tabID + u".json"));
}

void SearchWidget::DataStorage::loadHistory(const int limit)
{
const Path historyFilePath = makeDataFilePath(HISTORY_FILE_NAME);
const auto loadResult = ::loadHistory(historyFilePath, limit);
if (!loadResult)
{
LogMsg(tr("Failed to load Search UI history. File: \"%1\". Error: \"%2\"")
.arg(historyFilePath.toString(), loadResult.error()), Log::WARNING);
return;
}

emit historyLoaded(loadResult.value());
}

void SearchWidget::DataStorage::storeHistory(const QStringList &history)
{
const Path filePath = makeDataFilePath(HISTORY_FILE_NAME);
const auto saveResult = Utils::IO::saveToFile(filePath, history.join(u'\n').toUtf8());
if (!saveResult)
{
LogMsg(tr("Failed to save search history. File: \"%1\". Error: \"%2\"")
.arg(filePath.toString(), saveResult.error()), Log::WARNING);
}
}

void SearchWidget::DataStorage::removeHistory()
{
Utils::Fs::removeFile(makeDataFilePath(HISTORY_FILE_NAME));
}

#include "searchwidget.moc"
3 changes: 3 additions & 0 deletions src/gui/search/searchwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ class SearchWidget : public GUIApplicationComponent<QWidget>
QString generateTabID() const;
int addTab(const QString &tabID, SearchJobWidget *searchJobWdget);

void updateHistory(const QString &newSearchPattern);
void loadHistory();
void saveSession() const;
void restoreSession();

Expand All @@ -103,6 +105,7 @@ class SearchWidget : public GUIApplicationComponent<QWidget>

bool m_storeOpenedTabs = false;
bool m_storeOpenedTabsResults = false;
int m_historyLength = 0;

Utils::Thread::UniquePtr m_ioThread;

Expand Down

0 comments on commit 7221199

Please sign in to comment.