diff src/anime.cpp @ 6:1d82f6e04d7d

Update: add first parts to the settings dialog
author Paper <mrpapersonic@gmail.com>
date Wed, 16 Aug 2023 00:49:17 -0400
parents 190ded9438c0
children 07a9095eaeed
line wrap: on
line diff
--- a/src/anime.cpp	Sat Aug 12 13:10:34 2023 -0400
+++ b/src/anime.cpp	Wed Aug 16 00:49:17 2023 -0400
@@ -10,6 +10,7 @@
 #include "time_utils.h"
 #include "information.h"
 #include "ui_utils.h"
+#include <iostream>
 
 std::map<enum AnimeWatchingStatus, std::string> AnimeWatchingToStringMap = {
 	{CURRENT,   "Watching"},
@@ -74,6 +75,12 @@
 	duration = a.duration;
 }
 
+std::string Anime::GetUserPreferredTitle() {
+	if (title.english.empty())
+		return title.romaji;
+	return title.english;
+}
+
 void AnimeList::Add(Anime& anime) {
 	if (anime_id_to_anime.contains(anime.id))
 		return;
@@ -156,6 +163,58 @@
 
 /* ------------------------------------------------------------------------- */
 
+AnimeListWidgetDelegate::AnimeListWidgetDelegate(QObject* parent)
+	: QStyledItemDelegate (parent) {
+}
+
+QWidget *AnimeListWidgetDelegate::createEditor(QWidget *, const QStyleOptionViewItem &, const QModelIndex &) const
+{
+    // LOL
+    return nullptr;
+}
+
+void AnimeListWidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+    switch (index.column()) {
+		case AnimeListWidgetModel::AL_PROGRESS: {
+			const int progress = static_cast<int>(index.data(Qt::UserRole).toReal());
+			const int episodes = static_cast<int>(index.siblingAtColumn(AnimeListWidgetModel::AL_EPISODES).data(Qt::UserRole).toReal());
+
+			QStyleOptionViewItem customOption (option);
+			customOption.state.setFlag(QStyle::State_Enabled, true);
+
+			progress_bar.paint(painter, customOption, index.data().toString(), progress, episodes);
+			break;
+		}
+		default:
+			QStyledItemDelegate::paint(painter, option, index);
+			break;
+    }
+}
+
+AnimeListWidgetSortFilter::AnimeListWidgetSortFilter(QObject *parent)
+    : QSortFilterProxyModel(parent) {
+}
+
+bool AnimeListWidgetSortFilter::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:
+			return left.toString() < right.toString();
+	}
+}
+
 /* Thank you qBittorrent for having a great example of a
    widget model. */
 AnimeListWidgetModel::AnimeListWidgetModel (QWidget* parent, AnimeList* alist)
@@ -181,6 +240,8 @@
 				return tr("Anime title");
 			case AL_PROGRESS:
 				return tr("Progress");
+			case AL_EPISODES:
+				return tr("Episodes");
 			case AL_TYPE:
 				return tr("Type");
 			case AL_SCORE:
@@ -206,6 +267,7 @@
 			case AL_NOTES:
 				return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
 			case AL_PROGRESS:
+			case AL_EPISODES:
 			case AL_TYPE:
 			case AL_SCORE:
 			case AL_AVG_SCORE:
@@ -229,53 +291,75 @@
 QVariant AnimeListWidgetModel::data(const QModelIndex& index, int role) const {
 	if (!index.isValid())
 		return QVariant();
-	if (role == Qt::DisplayRole) {
-		switch (index.column()) {
-			case AL_TITLE:
-				return QString::fromWCharArray(list[index.row()].title.english.c_str());
-			case AL_PROGRESS:
-				return list[index.row()].progress;
-			case AL_SCORE:
-				return list[index.row()].score;
-			case AL_TYPE:
-				return QString::fromStdString(AnimeFormatToStringMap[list[index.row()].type]);
-			case AL_SEASON:
-				return QString::fromStdString(AnimeSeasonToStringMap[list[index.row()].season]) + " " + QString::number(list[index.row()].air_date.GetYear());
-			case AL_AVG_SCORE:
-				return list[index.row()].audience_score;
-			case AL_STARTED:
-				return list[index.row()].started.GetAsQDate();
-			case AL_COMPLETED:
-				return list[index.row()].completed.GetAsQDate();
-			case AL_UPDATED: {
-				if (list[index.row()].updated == 0)
-					return QString("-");
-				Time::Duration duration(Time::GetSystemTime() - list[index.row()].updated);
-				return QString::fromStdString(duration.AsRelativeString());
+	switch (role) {
+		case Qt::DisplayRole:
+			switch (index.column()) {
+				case AL_TITLE:
+					return QString::fromUtf8(list[index.row()].GetUserPreferredTitle().c_str());
+				case AL_PROGRESS:
+					return QString::number(list[index.row()].progress) + "/" + QString::number(list[index.row()].episodes);
+				case AL_EPISODES:
+					return list[index.row()].episodes;
+				case AL_SCORE:
+					return list[index.row()].score;
+				case AL_TYPE:
+					return QString::fromStdString(AnimeFormatToStringMap[list[index.row()].type]);
+				case AL_SEASON:
+					return QString::fromStdString(AnimeSeasonToStringMap[list[index.row()].season]) + " " + QString::number(list[index.row()].air_date.GetYear());
+				case AL_AVG_SCORE:
+					return QString::number(list[index.row()].audience_score) + "%";
+				case AL_STARTED:
+					return list[index.row()].started.GetAsQDate();
+				case AL_COMPLETED:
+					return list[index.row()].completed.GetAsQDate();
+				case AL_UPDATED: {
+					if (list[index.row()].updated == 0)
+						return QString("-");
+					Time::Duration duration(Time::GetSystemTime() - list[index.row()].updated);
+					return QString::fromUtf8(duration.AsRelativeString().c_str());
+				}
+				case AL_NOTES:
+					return QString::fromUtf8(list[index.row()].notes.c_str());
+				default:
+					return "";
 			}
-			case AL_NOTES:
-				return QString::fromWCharArray(list[index.row()].notes.c_str());
-			default:
-				return "";
-		}
-	} else if (role == Qt::TextAlignmentRole) {
-		switch (index.column()) {
-			case AL_TITLE:
-			case AL_NOTES:
-				return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
-			case AL_PROGRESS:
-			case AL_TYPE:
-			case AL_SCORE:
-			case AL_AVG_SCORE:
-				return QVariant(Qt::AlignCenter | Qt::AlignVCenter);
-			case AL_SEASON:
-			case AL_STARTED:
-			case AL_COMPLETED:
-			case AL_UPDATED:
-				return QVariant(Qt::AlignRight | Qt::AlignVCenter);
-			default:
-				break;
-		}
+			break;
+		case Qt::UserRole:
+			switch (index.column()) {
+				case AL_PROGRESS:
+					return list[index.row()].progress;
+				case AL_TYPE:
+					return list[index.row()].type;
+				case AL_SEASON:
+					return list[index.row()].air_date.GetAsQDate();
+				case AL_AVG_SCORE:
+					return list[index.row()].audience_score;
+				case AL_UPDATED:
+					return list[index.row()].updated;
+				default:
+					return data(index, Qt::DisplayRole);
+			}
+			break;
+		case Qt::TextAlignmentRole:
+			switch (index.column()) {
+				case AL_TITLE:
+				case AL_NOTES:
+					return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
+				case AL_PROGRESS:
+				case AL_EPISODES:
+				case AL_TYPE:
+				case AL_SCORE:
+				case AL_AVG_SCORE:
+					return QVariant(Qt::AlignCenter | Qt::AlignVCenter);
+				case AL_SEASON:
+				case AL_STARTED:
+				case AL_COMPLETED:
+				case AL_UPDATED:
+					return QVariant(Qt::AlignRight | Qt::AlignVCenter);
+				default:
+					break;
+			}
+			break;
 	}
 	return QVariant();
 }
@@ -285,27 +369,6 @@
 	emit dataChanged(index(i), index(i));
 }
 
-/* Most of this stuff is const and/or should be edited in the Information dialog
-
-bool AnimeListWidgetModel::setData(const QModelIndex &index, const QVariant &value, int role) {
-	if (!index.isValid() || role != Qt::DisplayRole)
-		return false;
-
-	Anime* const anime = &list[index.row()];
-
-	switch (index.column()) {
-		case AL_TITLE:
-			break;
-		case AL_CATEGORY:
-			break;
-		default:
-			return false;
-	}
-
-	return true;
-}
-*/
-
 int AnimeListWidget::VisibleColumnsCount() const {
     int count = 0;
 
@@ -325,6 +388,7 @@
 	setColumnHidden(AnimeListWidgetModel::AL_PROGRESS, false);
 	setColumnHidden(AnimeListWidgetModel::AL_SCORE, false);
 	setColumnHidden(AnimeListWidgetModel::AL_TITLE, false);
+	setColumnHidden(AnimeListWidgetModel::AL_EPISODES, true);
 	setColumnHidden(AnimeListWidgetModel::AL_AVG_SCORE, true);
 	setColumnHidden(AnimeListWidgetModel::AL_STARTED, true);
 	setColumnHidden(AnimeListWidgetModel::AL_COMPLETED, true);
@@ -370,29 +434,44 @@
 }
 
 void AnimeListWidget::DisplayListMenu() {
-	/* throw out any other garbage */
-    const QModelIndexList selected_items = selectionModel()->selectedRows();
-    if (selected_items.size() != 1 || !selected_items.first().isValid()) {
+    QMenu *menu = new QMenu(this);
+    menu->setAttribute(Qt::WA_DeleteOnClose);
+    menu->setTitle(tr("Column visibility"));
+    menu->setToolTipsVisible(true);
+
+    const QItemSelection selection = sort_model->mapSelectionToSource(selectionModel()->selection());
+    if (!selection.indexes().first().isValid()) {
         return;
 	}
 
-	const QModelIndex index = model->index(selected_items.first().row());
-	Anime* anime = model->GetAnimeFromIndex(index);
-	if (!anime) {
-		return;
-	}
+	QAction* action = menu->addAction("Information", [this, selection]{
+		const QModelIndex index = model->index(selection.indexes().first().row());
+		Anime* anime = model->GetAnimeFromIndex(index);
+		if (!anime) {
+			return;
+		}
 
+		InformationDialog* dialog = new InformationDialog(*anime, model, this);
+
+		dialog->show();
+		dialog->raise();
+		dialog->activateWindow();
+	});
+	menu->popup(QCursor::pos());
 }
 
 void AnimeListWidget::ItemDoubleClicked() {
 	/* throw out any other garbage */
-    const QModelIndexList selected_items = selectionModel()->selectedRows();
-    if (selected_items.size() != 1 || !selected_items.first().isValid()) {
+    const QItemSelection selection = sort_model->mapSelectionToSource(selectionModel()->selection());
+    if (!selection.indexes().first().isValid()) {
         return;
 	}
 
-	/* TODO: after we implement our sort model, we have to use mapToSource here... */
-	const QModelIndex index = model->index(selected_items.first().row());
+	const QModelIndex index = model->index(selection.indexes().first().row());
+	const QString title = index.siblingAtColumn(AnimeListWidgetModel::AL_TITLE).data(Qt::UserRole).toString();
+	QMessageBox box;
+	box.setText(QString::number(title.size()));
+	box.exec();
 	Anime* anime = model->GetAnimeFromIndex(index);
 	if (!anime) {
 		return;
@@ -407,10 +486,14 @@
 
 AnimeListWidget::AnimeListWidget(QWidget* parent, AnimeList* alist)
                                : QTreeView(parent) {
+	setItemDelegate(new AnimeListWidgetDelegate(this));
 	model = new AnimeListWidgetModel(parent, alist);
-	setModel(model);
+	sort_model = new AnimeListWidgetSortFilter(this);
+	sort_model->setSourceModel(model);
+	sort_model->setSortRole(Qt::UserRole);
+	setModel(sort_model);
 	setObjectName("listwidget");
-	setStyleSheet("QTreeView#listwidget{border-top:0px;}");
+	setStyleSheet("QTreeView#listwidget{border:0px;}");
 	setUniformRowHeights(true);
 	setAllColumnsShowFocus(false);
 	setSortingEnabled(true);
@@ -438,9 +521,11 @@
 
 AnimeListPage::AnimeListPage(QWidget* parent) : QTabWidget (parent) {
 	setDocumentMode(false);
+	setObjectName("animepage");
+	//setStyleSheet("QTabWidget#animepage{border-bottom:0px;border-left:0px;border-right:0px;}");
 	SyncAnimeList();
 	for (AnimeList& list : anime_lists) {
-		addTab(new AnimeListWidget(this, &list), QString::fromWCharArray(list.name.c_str()));
+		addTab(new AnimeListWidget(this, &list), QString::fromUtf8(list.name.c_str()));
 	}
 }
 
@@ -448,7 +533,6 @@
 	switch (session.config.service) {
 		case ANILIST: {
 			AniList anilist = AniList();
-			anilist.Authorize();
 			session.config.anilist.user_id = anilist.GetUserId(session.config.anilist.username);
 			FreeAnimeList();
 			anilist.UpdateAnimeList(&anime_lists, session.config.anilist.user_id);
@@ -460,14 +544,10 @@
 }
 
 void AnimeListPage::FreeAnimeList() {
-	if (anime_lists.size() > 0) {
-		/* FIXME: we may not need this, but to prevent memleaks
-		   we should keep it until we're sure we don't */
-		for (auto& list : anime_lists) {
-			list.Clear();
-		}
-		anime_lists.clear();
+	for (auto& list : anime_lists) {
+		list.Clear();
 	}
+	anime_lists.clear();
 }
 
 int AnimeListPage::GetTotalAnimeAmount() {