Mercurial > minori
view src/gui/pages/search.cc @ 327:b5d6c27c308f
anime: refactor Anime::SeriesSeason to Season class
ToLocalString has also been altered to take in both season
and year because lots of locales actually treat formatting
seasons differently! most notably is Russian which adds a
suffix at the end to notate seasons(??)
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Thu, 13 Jun 2024 01:49:18 -0400 |
parents | 5d3c9b31aa6e |
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); } }