diff src/gui/pages/anime_list.cc @ 370:ea3a74ed2ef9

*: hm, last commit wasn't quite finished?
author Paper <paper@tflc.us>
date Fri, 25 Jul 2025 10:22:04 -0400
parents 886f66775f31
children f7bb2978de48
line wrap: on
line diff
--- a/src/gui/pages/anime_list.cc	Fri Jul 25 10:16:02 2025 -0400
+++ b/src/gui/pages/anime_list.cc	Fri Jul 25 10:22:04 2025 -0400
@@ -14,38 +14,42 @@
 #include "core/session.h"
 #include "core/strings.h"
 #include "core/time.h"
-#include "library/library.h"
 #include "gui/dialog/information.h"
 #include "gui/translate/anime.h"
+#include "library/library.h"
 #include "services/services.h"
 
 #include <QDate>
 #include <QDebug>
+#include <QDesktopServices>
 #include <QHBoxLayout>
 #include <QHeaderView>
 #include <QMenu>
 #include <QProgressBar>
+#include <QRunnable>
 #include <QShortcut>
 #include <QStylePainter>
 #include <QStyledItemDelegate>
 #include <QThreadPool>
-#include <QRunnable>
 #include <QTreeView>
-#include <QDesktopServices>
 #include <QUrl>
 
 #include <iostream>
 #include <vector>
 
-AnimeListPageUpdateEntryThread::AnimeListPageUpdateEntryThread(QObject* parent) : QThread(parent) {}
+AnimeListPageUpdateEntryThread::AnimeListPageUpdateEntryThread(QObject *parent) : QThread(parent)
+{
+}
 
-void AnimeListPageUpdateEntryThread::AddToQueue(int id) {
+void AnimeListPageUpdateEntryThread::AddToQueue(int id)
+{
 	const std::lock_guard<std::mutex> guard(queue_mutex_);
 	queue_.push(id);
 }
 
 /* processes the queue... */
-void AnimeListPageUpdateEntryThread::run() {
+void AnimeListPageUpdateEntryThread::run()
+{
 	queue_mutex_.lock();
 	while (!queue_.empty() && !isInterruptionRequested()) {
 		int id = queue_.front();
@@ -63,10 +67,12 @@
 	emit NeedRefresh();
 }
 
-AnimeListPageSortFilter::AnimeListPageSortFilter(QObject* parent) : QSortFilterProxyModel(parent) {
+AnimeListPageSortFilter::AnimeListPageSortFilter(QObject *parent) : QSortFilterProxyModel(parent)
+{
 }
 
-bool AnimeListPageSortFilter::lessThan(const QModelIndex& l, const QModelIndex& r) const {
+bool AnimeListPageSortFilter::lessThan(const QModelIndex &l, const QModelIndex &r) const
+{
 	QVariant left = sourceModel()->data(l, sortRole());
 	QVariant right = sourceModel()->data(r, sortRole());
 
@@ -83,22 +89,26 @@
 
 /* -------------------------------------------------- */
 
-AnimeListPageModel::AnimeListPageModel(QObject* parent, Anime::ListStatus _status) : QAbstractListModel(parent) {
+AnimeListPageModel::AnimeListPageModel(QObject *parent, Anime::ListStatus _status) : QAbstractListModel(parent)
+{
 	status = _status;
 	return;
 }
 
-int AnimeListPageModel::rowCount(const QModelIndex& parent) const {
+int AnimeListPageModel::rowCount(const QModelIndex &parent) const
+{
 	return list.size();
 	(void)(parent);
 }
 
-int AnimeListPageModel::columnCount(const QModelIndex& parent) const {
+int AnimeListPageModel::columnCount(const QModelIndex &parent) const
+{
 	return NB_COLUMNS;
 	(void)(parent);
 }
 
-QVariant AnimeListPageModel::headerData(const int section, const Qt::Orientation orientation, const int role) const {
+QVariant AnimeListPageModel::headerData(const int section, const Qt::Orientation orientation, const int role) const
+{
 	if (role == Qt::DisplayRole) {
 		switch (section) {
 			case AL_TITLE: return tr("Anime title");
@@ -133,7 +143,8 @@
 	return QAbstractListModel::headerData(section, orientation, role);
 }
 
-QVariant AnimeListPageModel::data(const QModelIndex& index, int role) const {
+QVariant AnimeListPageModel::data(const QModelIndex &index, int role) const
+{
 	if (!index.isValid())
 		return QVariant();
 	switch (role) {
@@ -142,7 +153,7 @@
 				case AL_TITLE: return Strings::ToQString(list[index.row()].GetUserPreferredTitle());
 				case AL_PROGRESS:
 					return QString::number(list[index.row()].GetUserProgress()) + "/" +
-						   QString::number(list[index.row()].GetEpisodes());
+					       QString::number(list[index.row()].GetEpisodes());
 				case AL_EPISODES: return list[index.row()].GetEpisodes();
 				case AL_SCORE: return Strings::ToQString(list[index.row()].GetUserPresentableScore());
 				case AL_TYPE: return Strings::ToQString(Translate::ToString(list[index.row()].GetFormat()));
@@ -153,7 +164,8 @@
 				case AL_UPDATED: {
 					if (list[index.row()].GetUserTimeUpdated() == 0)
 						return QString("-");
-					return Strings::ToQString(Time::GetSecondsAsRelativeString(Time::GetSystemTime() - list[index.row()].GetUserTimeUpdated()));
+					return Strings::ToQString(Time::GetSecondsAsRelativeString(Time::GetSystemTime() -
+					                                                           list[index.row()].GetUserTimeUpdated()));
 				}
 				case AL_NOTES: return Strings::ToQString(list[index.row()].GetUserNotes());
 				default: return "";
@@ -190,11 +202,13 @@
 	return QVariant();
 }
 
-Anime::Anime* AnimeListPageModel::GetAnimeFromIndex(QModelIndex index) {
+Anime::Anime *AnimeListPageModel::GetAnimeFromIndex(QModelIndex index)
+{
 	return &list.at(index.row());
 }
 
-void AnimeListPageModel::RefreshList() {
+void AnimeListPageModel::RefreshList()
+{
 	/* equivalent to hasChildren()... */
 	if (!rowCount(index(0))) {
 		beginInsertRows(QModelIndex(), 0, 0);
@@ -205,7 +219,7 @@
 
 	list.clear();
 
-	for (const auto& a : Anime::db.items)
+	for (const auto &a : Anime::db.items)
 		if (a.second.IsInUserList() && a.second.GetUserStatus() == status)
 			list.push_back(a.second);
 
@@ -214,7 +228,8 @@
 
 /* ----------------------------------------------------------------- */
 
-int AnimeListPage::VisibleColumnsCount() const {
+int AnimeListPage::VisibleColumnsCount() const
+{
 	int count = 0;
 
 	for (int i = 0, end = tree_view->header()->count(); i < end; i++) {
@@ -225,7 +240,8 @@
 	return count;
 }
 
-void AnimeListPage::SetColumnDefaults() {
+void AnimeListPage::SetColumnDefaults()
+{
 	tree_view->setColumnHidden(AnimeListPageModel::AL_SEASON, false);
 	tree_view->setColumnHidden(AnimeListPageModel::AL_TYPE, false);
 	tree_view->setColumnHidden(AnimeListPageModel::AL_UPDATED, false);
@@ -240,7 +256,8 @@
 	tree_view->setColumnHidden(AnimeListPageModel::AL_NOTES, true);
 }
 
-void AnimeListPage::UpdateAnime(int id) {
+void AnimeListPage::UpdateAnime(int id)
+{
 	/* this ought to just add to the thread's buffer. */
 	if (update_entry_thread_.isRunning())
 		update_entry_thread_.requestInterruption();
@@ -249,14 +266,16 @@
 	update_entry_thread_.start();
 }
 
-void AnimeListPage::RemoveAnime(int id) {
-	Anime::Anime& anime = Anime::db.items[id];
+void AnimeListPage::RemoveAnime(int id)
+{
+	Anime::Anime &anime = Anime::db.items[id];
 	anime.RemoveFromUserList();
 	Refresh();
 }
 
-void AnimeListPage::DisplayColumnHeaderMenu() {
-	QMenu* menu = new QMenu(this);
+void AnimeListPage::DisplayColumnHeaderMenu()
+{
+	QMenu *menu = new QMenu(this);
 	menu->setAttribute(Qt::WA_DeleteOnClose);
 	menu->setTitle(tr("Column visibility"));
 	menu->setToolTipsVisible(true);
@@ -265,9 +284,9 @@
 		if (i == AnimeListPageModel::AL_TITLE)
 			continue;
 		const auto column_name =
-			sort_models[tab_bar->currentIndex()]->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
+		    sort_models[tab_bar->currentIndex()]->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
 
-		QAction* action = menu->addAction(column_name, this, [this, i](const bool checked) {
+		QAction *action = menu->addAction(column_name, this, [this, i](const bool checked) {
 			if (!checked && (VisibleColumnsCount() <= 1))
 				return;
 
@@ -293,18 +312,19 @@
 	menu->popup(QCursor::pos());
 }
 
-void AnimeListPage::DisplayListMenu() {
+void AnimeListPage::DisplayListMenu()
+{
 	QMenu *const menu = new QMenu(this);
 	menu->setAttribute(Qt::WA_DeleteOnClose);
 	menu->setToolTipsVisible(true);
 
-	AnimeListPageModel* source_model =
-		reinterpret_cast<AnimeListPageModel*>(sort_models[tab_bar->currentIndex()]->sourceModel());
+	AnimeListPageModel *source_model =
+	    reinterpret_cast<AnimeListPageModel *>(sort_models[tab_bar->currentIndex()]->sourceModel());
 	const QItemSelection selection =
-		sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection());
+	    sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection());
 
-	std::set<Anime::Anime*> animes;
-	for (const auto& index : selection.indexes()) {
+	std::set<Anime::Anime *> animes;
+	for (const auto &index : selection.indexes()) {
 		if (!index.isValid())
 			continue;
 
@@ -372,8 +392,9 @@
 		Anime::Anime *anime = *animes.begin();
 
 		menu->addAction(tr("Information"), [this, anime] {
-			InformationDialog* dialog = new InformationDialog(
-				anime, [this](Anime::Anime* anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, this);
+			InformationDialog *dialog = new InformationDialog(
+			    anime, [this](Anime::Anime *anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO,
+			    this);
 
 			dialog->show();
 			dialog->raise();
@@ -384,17 +405,18 @@
 		menu->addSeparator();
 
 		menu->addAction(tr("Edit"), [this, anime] {
-			InformationDialog* dialog = new InformationDialog(
-				anime, [this](Anime::Anime* anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MY_LIST, this);
+			InformationDialog *dialog = new InformationDialog(
+			    anime, [this](Anime::Anime *anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MY_LIST,
+			    this);
 
 			dialog->show();
 			dialog->raise();
 			dialog->activateWindow();
 			connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater);
 		});
-		menu->addAction(tr("Delete from list..."), [this, anime] {
-			RemoveAnime(anime->GetId());
-		}, QKeySequence(QKeySequence::Delete));
+		menu->addAction(
+		    tr("Delete from list..."), [this, anime] { RemoveAnime(anime->GetId()); },
+		    QKeySequence(QKeySequence::Delete));
 
 		menu->addSeparator();
 
@@ -405,9 +427,7 @@
 
 			QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(path.value().u8string())));
 		});
-		menu->addAction(tr("Scan available episodes"), [this, anime] {
-			Library::db.Refresh(anime->GetId());
-		});
+		menu->addAction(tr("Scan available episodes"), [this, anime] { Library::db.Refresh(anime->GetId()); });
 
 		menu->addSeparator();
 
@@ -428,38 +448,47 @@
 			menu->addAction(tr("Play last episode (#%1)").arg(progress), [this, anime, progress] {
 				const int id = anime->GetId();
 
-				if (Library::db.items.find(id) == Library::db.items.end()
-					|| Library::db.items[id].find(progress) == Library::db.items[id].end())
+				if (Library::db.items.find(id) == Library::db.items.end() ||
+				    Library::db.items[id].find(progress) == Library::db.items[id].end())
 					return;
 
-				QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress].u8string())));
+				QDesktopServices::openUrl(
+				    QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress].u8string())));
 			});
 		}
 
 		if (progress < episodes) {
-			menu->addAction(tr("Play next episode (#%1)").arg(progress + 1), [this, anime, progress] {
-				const int id = anime->GetId();
+			menu->addAction(
+			    tr("Play next episode (#%1)").arg(progress + 1),
+			    [this, anime, progress] {
+				    const int id = anime->GetId();
 
-				if (Library::db.items.find(id) == Library::db.items.end()
-					|| Library::db.items[id].find(progress + 1) == Library::db.items[id].end())
-					return;
+				    if (Library::db.items.find(id) == Library::db.items.end() ||
+				        Library::db.items[id].find(progress + 1) == Library::db.items[id].end())
+					    return;
 
-				QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress + 1].u8string())));
-			}, QKeySequence(Qt::CTRL | Qt::Key_N));
+				    QDesktopServices::openUrl(
+				        QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress + 1].u8string())));
+			    },
+			    QKeySequence(Qt::CTRL | Qt::Key_N));
 		}
 
-		menu->addAction(tr("Play random episode"), [this, anime, episodes] {
-			const int id = anime->GetId();
+		menu->addAction(
+		    tr("Play random episode"),
+		    [this, anime, episodes] {
+			    const int id = anime->GetId();
 
-			std::uniform_int_distribution<int> distrib(1, episodes);
-			const int episode = distrib(session.gen);
+			    std::uniform_int_distribution<int> distrib(1, episodes);
+			    const int episode = distrib(session.gen);
 
-			if (Library::db.items.find(id) == Library::db.items.end()
-				|| Library::db.items[id].find(episode) == Library::db.items[id].end())
-				return;
+			    if (Library::db.items.find(id) == Library::db.items.end() ||
+			        Library::db.items[id].find(episode) == Library::db.items[id].end())
+				    return;
 
-			QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][episode].u8string())));
-		}, QKeySequence(Qt::CTRL | Qt::Key_R));
+			    QDesktopServices::openUrl(
+			        QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][episode].u8string())));
+		    },
+		    QKeySequence(Qt::CTRL | Qt::Key_R));
 
 		menu->popup(QCursor::pos());
 	} else {
@@ -467,22 +496,23 @@
 	}
 }
 
-void AnimeListPage::ItemDoubleClicked() {
+void AnimeListPage::ItemDoubleClicked()
+{
 	/* throw out any other garbage */
 	const QItemSelection selection =
-		sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection());
+	    sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection());
 	if (!selection.indexes().first().isValid()) {
 		return;
 	}
 
-	AnimeListPageModel* source_model =
-		reinterpret_cast<AnimeListPageModel*>(sort_models[tab_bar->currentIndex()]->sourceModel());
+	AnimeListPageModel *source_model =
+	    reinterpret_cast<AnimeListPageModel *>(sort_models[tab_bar->currentIndex()]->sourceModel());
 
 	const QModelIndex index = source_model->index(selection.indexes().first().row());
-	Anime::Anime& anime = Anime::db.items[source_model->GetAnimeFromIndex(index)->GetId()];
+	Anime::Anime &anime = Anime::db.items[source_model->GetAnimeFromIndex(index)->GetId()];
 
-	InformationDialog* dialog = new InformationDialog(
-		&anime, [this](Anime::Anime* anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, this);
+	InformationDialog *dialog = new InformationDialog(
+	    &anime, [this](Anime::Anime *anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, this);
 
 	dialog->show();
 	dialog->raise();
@@ -490,25 +520,29 @@
 	connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater);
 }
 
-void AnimeListPage::RefreshList() {
+void AnimeListPage::RefreshList()
+{
 	for (unsigned int i = 0; i < sort_models.size(); i++)
-		reinterpret_cast<AnimeListPageModel*>(sort_models[i]->sourceModel())->RefreshList();
+		reinterpret_cast<AnimeListPageModel *>(sort_models[i]->sourceModel())->RefreshList();
 }
 
-void AnimeListPage::RefreshTabs() {
+void AnimeListPage::RefreshTabs()
+{
 	for (unsigned int i = 0; i < sort_models.size(); i++)
 		tab_bar->setTabText(i, Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])) + " (" +
-								   QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")");
+		                           QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")");
 }
 
-void AnimeListPage::Refresh() {
+void AnimeListPage::Refresh()
+{
 	RefreshList();
 	RefreshTabs();
 }
 
 /* -------- QTabWidget replication begin --------- */
 
-void AnimeListPage::InitBasicStyle(QStyleOptionTabWidgetFrame* option) const {
+void AnimeListPage::InitBasicStyle(QStyleOptionTabWidgetFrame *option) const
+{
 	if (!option)
 		return;
 
@@ -518,7 +552,8 @@
 	option->tabBarRect = tab_bar->geometry();
 }
 
-void AnimeListPage::InitStyle(QStyleOptionTabWidgetFrame* option) const {
+void AnimeListPage::InitStyle(QStyleOptionTabWidgetFrame *option) const
+{
 	if (!option)
 		return;
 
@@ -539,7 +574,8 @@
 	option->lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, this);
 }
 
-void AnimeListPage::SetupLayout() {
+void AnimeListPage::SetupLayout()
+{
 	QStyleOptionTabWidgetFrame option;
 	InitStyle(&option);
 
@@ -552,7 +588,8 @@
 	tree_view->parentWidget()->setGeometry(contentsRect);
 }
 
-void AnimeListPage::paintEvent(QPaintEvent*) {
+void AnimeListPage::paintEvent(QPaintEvent *)
+{
 	QStylePainter p(this);
 
 	QStyleOptionTabWidgetFrame opt;
@@ -561,26 +598,29 @@
 	p.drawPrimitive(QStyle::PE_FrameTabWidget, opt);
 }
 
-void AnimeListPage::resizeEvent(QResizeEvent* e) {
+void AnimeListPage::resizeEvent(QResizeEvent *e)
+{
 	QWidget::resizeEvent(e);
 	SetupLayout();
 }
 
-void AnimeListPage::showEvent(QShowEvent*) {
+void AnimeListPage::showEvent(QShowEvent *)
+{
 	SetupLayout();
 	Refresh();
 }
 
 /* --------- QTabWidget replication end ---------- */
 
-AnimeListPage::AnimeListPage(QWidget* parent) : QWidget(parent) {
+AnimeListPage::AnimeListPage(QWidget *parent) : QWidget(parent)
+{
 	/* Tab bar */
 	tab_bar = new QTabBar(this);
 	tab_bar->setExpanding(false);
 	tab_bar->setDrawBase(false);
 
 	/* Tree view... */
-	QWidget* tree_widget = new QWidget(this);
+	QWidget *tree_widget = new QWidget(this);
 	tree_view = new QTreeView(tree_widget);
 	tree_view->setUniformRowHeights(true);
 	tree_view->setAllColumnsShowFocus(false);
@@ -594,7 +634,7 @@
 
 	for (unsigned int i = 0; i < sort_models.size(); i++) {
 		tab_bar->addTab(Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])) + " (" +
-						QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")");
+		                QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")");
 		sort_models[i] = new AnimeListPageSortFilter(tree_view);
 		sort_models[i]->setSourceModel(new AnimeListPageModel(this, Anime::ListStatuses[i]));
 		sort_models[i]->setSortRole(Qt::UserRole);
@@ -615,7 +655,7 @@
 	tree_view->setColumnWidth(AnimeListPageModel::AL_UPDATED, 100);
 	tree_view->setColumnWidth(AnimeListPageModel::AL_NOTES, 100);
 
-	QHBoxLayout* layout = new QHBoxLayout(tree_widget);
+	QHBoxLayout *layout = new QHBoxLayout(tree_widget);
 	layout->addWidget(tree_view);
 	layout->setContentsMargins(0, 0, 0, 0);
 
@@ -625,10 +665,10 @@
 
 	/* Enter & return keys */
 	connect(new QShortcut(Qt::Key_Return, tree_view, nullptr, nullptr, Qt::WidgetShortcut), &QShortcut::activated, this,
-			&AnimeListPage::ItemDoubleClicked);
+	        &AnimeListPage::ItemDoubleClicked);
 
 	connect(new QShortcut(Qt::Key_Enter, tree_view, nullptr, nullptr, Qt::WidgetShortcut), &QShortcut::activated, this,
-			&AnimeListPage::ItemDoubleClicked);
+	        &AnimeListPage::ItemDoubleClicked);
 
 	tree_view->header()->setStretchLastSection(false);
 	tree_view->header()->setContextMenuPolicy(Qt::CustomContextMenu);