Mercurial > minori
view src/gui/pages/search.cc @ 337:a7d4e5107531
dep/animone: REFACTOR ALL THE THINGS
1: animone now has its own syntax divergent from anisthesia,
making different platforms actually have their own sections
2: process names in animone are now called `comm' (this will
probably break things). this is what its called in bsd/linux
so I'm just going to use it everywhere
3: the X11 code now checks for the existence of a UTF-8 window title
and passes it if available
4: ANYTHING THATS NOT LINUX IS 100% UNTESTED AND CAN AND WILL BREAK!
I still actually need to test the bsd code. to be honest I'm probably
going to move all of the bsds into separate files because they're
all essentially different operating systems at this point
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Wed, 19 Jun 2024 12:51:15 -0400 |
parents | b5d6c27c308f |
children | 8d45d892be88 |
line wrap: on
line source
#include "gui/pages/search.h" #include "core/anime.h" #include "core/anime_db.h" #include "core/filesystem.h" #include "core/http.h" #include "core/session.h" #include "core/strings.h" #include "gui/dialog/information.h" #include "gui/translate/anime.h" #include "gui/widgets/text.h" #include "services/services.h" #include "track/media.h" #include <QDate> #include <QHeaderView> #include <QMenu> #include <QToolBar> #include <QTreeView> #include <QVBoxLayout> #include <algorithm> #include <fstream> #include <iostream> #include <sstream> #include "anitomy/anitomy.h" #include "pugixml.hpp" SearchPageSearchThread::SearchPageSearchThread(QObject* parent) : QThread(parent) { } void SearchPageSearchThread::SetSearch(const std::string& search) { search_ = search; } void SearchPageSearchThread::run() { emit GotResults(Services::Search(search_)); } SearchPageListSortFilter::SearchPageListSortFilter(QObject* parent) : QSortFilterProxyModel(parent) { } bool SearchPageListSortFilter::lessThan(const QModelIndex& l, const QModelIndex& r) const { QVariant left = sourceModel()->data(l, sortRole()); QVariant right = sourceModel()->data(r, sortRole()); switch (left.userType()) { case QMetaType::Int: case QMetaType::UInt: case QMetaType::LongLong: case QMetaType::ULongLong: return left.toInt() < right.toInt(); case QMetaType::QDate: return left.toDate() < right.toDate(); case QMetaType::QString: default: // meh return QString::compare(left.toString(), right.toString(), Qt::CaseInsensitive) < 0; } } /* -------------------------------------------- */ SearchPageListModel::SearchPageListModel(QObject* parent) : QAbstractListModel(parent) { } void SearchPageListModel::ParseSearch(const std::vector<int>& ids) { /* hack!!! */ if (!rowCount(index(0))) { beginInsertRows(QModelIndex(), 0, 0); endInsertRows(); } beginResetModel(); this->ids = ids; endResetModel(); } int SearchPageListModel::rowCount(const QModelIndex& parent) const { return ids.size(); (void)(parent); } int SearchPageListModel::columnCount(const QModelIndex& parent) const { return NB_COLUMNS; (void)(parent); } QVariant SearchPageListModel::headerData(const int section, const Qt::Orientation orientation, const int role) const { switch (role) { case Qt::DisplayRole: { switch (section) { case SR_TITLE: return tr("Anime title"); case SR_EPISODES: return tr("Episode"); case SR_TYPE: return tr("Type"); case SR_SCORE: return tr("Score"); case SR_SEASON: return tr("Season"); default: return {}; } break; } case Qt::TextAlignmentRole: { switch (section) { case SR_TITLE: return QVariant(Qt::AlignLeft | Qt::AlignVCenter); case SR_TYPE: return QVariant(Qt::AlignHCenter | Qt::AlignVCenter); case SR_EPISODES: case SR_SCORE: case SR_SEASON: return QVariant(Qt::AlignRight | Qt::AlignVCenter); default: return {}; } break; } } return QAbstractListModel::headerData(section, orientation, role); } QVariant SearchPageListModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); const Anime::Anime& anime = Anime::db.items[ids[index.row()]]; switch (role) { case Qt::DisplayRole: switch (index.column()) { case SR_TITLE: return Strings::ToQString(anime.GetUserPreferredTitle()); case SR_TYPE: return Strings::ToQString(Translate::ToLocalString(anime.GetFormat())); case SR_EPISODES: return anime.GetEpisodes(); case SR_SCORE: return QString::number(anime.GetAudienceScore()) + "%"; case SR_SEASON: return Strings::ToQString(Translate::ToLocalString(anime.GetSeason())); default: return {}; } break; case Qt::UserRole: switch (index.column()) { case SR_SCORE: return anime.GetAudienceScore(); case SR_EPISODES: return anime.GetEpisodes(); case SR_SEASON: return anime.GetStartedDate().GetAsQDate(); /* We have to use this to work around some stupid * "conversion ambiguous" error on Linux */ default: return data(index, Qt::DisplayRole); } break; case Qt::SizeHintRole: { switch (index.column()) { default: { /* max horizontal size of 100, height size = size of current font */ const QString d = data(index, Qt::DisplayRole).toString(); const QFontMetrics metric = QFontMetrics(QFont()); return QSize(std::max(metric.boundingRect(d).width(), 100), metric.height()); } } break; } case Qt::TextAlignmentRole: return headerData(index.column(), Qt::Horizontal, Qt::TextAlignmentRole); } return QVariant(); } Qt::ItemFlags SearchPageListModel::flags(const QModelIndex& index) const { if (!index.isValid()) return Qt::NoItemFlags; return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } Anime::Anime* SearchPageListModel::GetAnimeFromIndex(const QModelIndex& index) const { return &Anime::db.items[ids[index.row()]]; } void SearchPage::DisplayListMenu() { QMenu* menu = new QMenu(this); menu->setAttribute(Qt::WA_DeleteOnClose); menu->setToolTipsVisible(true); const QItemSelection selection = sort_model->mapSelectionToSource(treeview->selectionModel()->selection()); bool add_to_list_enable = true; std::set<Anime::Anime*> animes; for (const auto& index : selection.indexes()) { if (!index.isValid()) continue; Anime::Anime* anime = model->GetAnimeFromIndex(index); if (anime) { animes.insert(anime); if (anime->IsInUserList()) add_to_list_enable = false; } } menu->addAction(tr("Information"), [this, animes] { for (auto& anime : animes) { InformationDialog* dialog = new InformationDialog( anime, [this](Anime::Anime* anime) { // UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, this); dialog->show(); dialog->raise(); dialog->activateWindow(); } }); menu->addSeparator(); { QMenu* submenu = menu->addMenu(tr("Add to list...")); for (const auto& status : Anime::ListStatuses) { submenu->addAction(Strings::ToQString(Translate::ToLocalString(status)), [animes, status] { for (auto& anime : animes) { if (!anime->IsInUserList()) anime->AddToUserList(); anime->SetUserStatus(status); Services::UpdateAnimeEntry(anime->GetId()); } }); } submenu->setEnabled(add_to_list_enable); } menu->popup(QCursor::pos()); } void SearchPage::ItemDoubleClicked() { /* throw out any other garbage */ const QItemSelection selection = sort_model->mapSelectionToSource(treeview->selectionModel()->selection()); if (!selection.indexes().first().isValid()) return; const QModelIndex index = model->index(selection.indexes().first().row()); Anime::Anime* anime = model->GetAnimeFromIndex(index); InformationDialog* dialog = new InformationDialog( anime, [this](Anime::Anime* anime) { // UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, this); dialog->show(); dialog->raise(); dialog->activateWindow(); } SearchPage::SearchPage(QWidget* parent) : QFrame(parent) { setFrameShape(QFrame::Box); setFrameShadow(QFrame::Sunken); QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); { /* Toolbar */ QToolBar* toolbar = new QToolBar(this); toolbar->setMovable(false); { QLineEdit* line_edit = new QLineEdit("", toolbar); connect(line_edit, &QLineEdit::returnPressed, this, [this, line_edit] { /* static thread here. */ if (thread_.isRunning()) thread_.exit(1); /* fail */ thread_.SetSearch(Strings::ToUtf8String(line_edit->text())); thread_.start(); }); connect(&thread_, &SearchPageSearchThread::GotResults, this, [this](const std::vector<int>& search) { model->ParseSearch(search); }); toolbar->addWidget(line_edit); } layout->addWidget(toolbar); } { QFrame* line = new QFrame(this); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); line->setLineWidth(1); layout->addWidget(line); } { treeview = new QTreeView(this); treeview->setUniformRowHeights(true); treeview->setAllColumnsShowFocus(false); treeview->setAlternatingRowColors(true); treeview->setSortingEnabled(true); treeview->setSelectionMode(QAbstractItemView::ExtendedSelection); treeview->setItemsExpandable(false); treeview->setRootIsDecorated(false); treeview->setContextMenuPolicy(Qt::CustomContextMenu); treeview->setFrameShape(QFrame::NoFrame); { sort_model = new SearchPageListSortFilter(treeview); model = new SearchPageListModel(treeview); sort_model->setSourceModel(model); sort_model->setSortRole(Qt::UserRole); sort_model->setSortCaseSensitivity(Qt::CaseInsensitive); treeview->setModel(sort_model); } // set column sizes treeview->setColumnWidth(SearchPageListModel::SR_TITLE, 400); treeview->setColumnWidth(SearchPageListModel::SR_TYPE, 60); treeview->setColumnWidth(SearchPageListModel::SR_EPISODES, 60); treeview->setColumnWidth(SearchPageListModel::SR_SCORE, 60); treeview->setColumnWidth(SearchPageListModel::SR_SEASON, 100); treeview->header()->setStretchLastSection(false); /* Double click stuff */ connect(treeview, &QAbstractItemView::doubleClicked, this, &SearchPage::ItemDoubleClicked); connect(treeview, &QWidget::customContextMenuRequested, this, &SearchPage::DisplayListMenu); layout->addWidget(treeview); } }