mirror-chatterino2/src/widgets/helper/SearchPopup.cpp

173 lines
4.5 KiB
C++

#include "SearchPopup.hpp"
#include <QHBoxLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include "common/Channel.hpp"
#include "messages/Message.hpp"
#include "messages/search/AuthorPredicate.hpp"
#include "messages/search/LinkPredicate.hpp"
#include "messages/search/SubstringPredicate.hpp"
#include "widgets/helper/ChannelView.hpp"
namespace chatterino {
ChannelPtr SearchPopup::filter(const QString &text, const QString &channelName,
const LimitedQueueSnapshot<MessagePtr> &snapshot)
{
ChannelPtr channel(new Channel(channelName, Channel::Type::None));
// Parse predicates from tags in "text"
auto predicates = parsePredicates(text);
// Check for every message whether it fulfills all predicates that have
// been registered
for (size_t i = 0; i < snapshot.size(); ++i)
{
MessagePtr message = snapshot[i];
bool accept = true;
for (const auto &pred : predicates)
{
// Discard the message as soon as one predicate fails
if (!pred->appliesTo(*message))
{
accept = false;
break;
}
}
// If all predicates match, add the message to the channel
if (accept)
channel->addMessage(message);
}
return channel;
}
SearchPopup::SearchPopup()
{
this->initLayout();
this->resize(400, 600);
}
void SearchPopup::setChannel(const ChannelPtr &channel)
{
this->channelName_ = channel->getName();
this->snapshot_ = channel->getMessageSnapshot();
this->search();
this->updateWindowTitle();
}
void SearchPopup::updateWindowTitle()
{
this->setWindowTitle("Searching in " + this->channelName_ + "s history");
}
void SearchPopup::search()
{
this->channelView_->setChannel(filter(this->searchInput_->text(),
this->channelName_, this->snapshot_));
}
void SearchPopup::initLayout()
{
// VBOX
{
QVBoxLayout *layout1 = new QVBoxLayout(this);
layout1->setMargin(0);
layout1->setSpacing(0);
// HBOX
{
QHBoxLayout *layout2 = new QHBoxLayout(this);
layout2->setMargin(8);
layout2->setSpacing(8);
// SEARCH INPUT
{
this->searchInput_ = new QLineEdit(this);
layout2->addWidget(this->searchInput_);
QObject::connect(this->searchInput_, &QLineEdit::returnPressed,
[this] { this->search(); });
}
// SEARCH BUTTON
{
QPushButton *searchButton = new QPushButton(this);
searchButton->setText("Search");
layout2->addWidget(searchButton);
QObject::connect(searchButton, &QPushButton::clicked,
[this] { this->search(); });
}
layout1->addLayout(layout2);
}
// CHANNELVIEW
{
this->channelView_ = new ChannelView(this);
layout1->addWidget(this->channelView_);
}
this->setLayout(layout1);
}
}
std::vector<std::unique_ptr<MessagePredicate>> SearchPopup::parsePredicates(
const QString &input)
{
static QRegularExpression predicateRegex(R"(^(\w+):([\w,]+)$)");
auto predicates = std::vector<std::unique_ptr<MessagePredicate>>();
auto words = input.split(' ', QString::SkipEmptyParts);
auto authors = QStringList();
for (auto it = words.begin(); it != words.end();)
{
if (auto match = predicateRegex.match(*it); match.hasMatch())
{
QString name = match.captured(1);
QString value = match.captured(2);
bool remove = true;
// match predicates
if (name == "from")
{
authors.append(value);
}
else if (name == "has" && value == "link")
{
predicates.push_back(std::make_unique<LinkPredicate>());
}
else
{
remove = false;
}
// remove or advance
it = remove ? words.erase(it) : ++it;
}
else
{
++it;
}
}
if (!authors.empty())
predicates.push_back(std::make_unique<AuthorPredicate>(authors));
if (!words.empty())
predicates.push_back(
std::make_unique<SubstringPredicate>(words.join(" ")));
return predicates;
}
} // namespace chatterino