Mercurial > minori
diff src/anime.cpp @ 7:07a9095eaeed
Update
Refactored some code, moved some around
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Thu, 24 Aug 2023 23:11:38 -0400 |
parents | 1d82f6e04d7d |
children |
line wrap: on
line diff
--- a/src/anime.cpp Wed Aug 16 00:49:17 2023 -0400 +++ b/src/anime.cpp Thu Aug 24 23:11:38 2023 -0400 @@ -1,16 +1,16 @@ +/* + * anime.cpp: defining of custom anime-related + * datatypes & variables +*/ #include <chrono> #include <string> #include <vector> #include <cmath> -#include "window.h" +#include <algorithm> #include "anilist.h" -#include "config.h" #include "anime.h" #include "date.h" -#include "time_utils.h" -#include "information.h" -#include "ui_utils.h" -#include <iostream> +#include "session.h" std::map<enum AnimeWatchingStatus, std::string> AnimeWatchingToStringMap = { {CURRENT, "Watching"}, @@ -76,9 +76,31 @@ } std::string Anime::GetUserPreferredTitle() { - if (title.english.empty()) - return title.romaji; - return title.english; + switch (session.config.anime_list.language) { + case NATIVE: + return (title.native.empty()) ? title.romaji : title.native; + case ENGLISH: + return (title.english.empty()) ? title.romaji : title.english; + default: + return title.romaji; + } +} + +std::vector<std::string> Anime::GetTitleSynonyms() { + std::vector<std::string> result; +#define IN_VECTOR(v, k) \ + (std::count(v.begin(), v.end(), k)) +#define ADD_TO_SYNONYMS(v, k) \ + if (!k.empty() && !IN_VECTOR(v, k) && k != GetUserPreferredTitle()) v.push_back(k) + ADD_TO_SYNONYMS(result, title.english); + ADD_TO_SYNONYMS(result, title.romaji); + ADD_TO_SYNONYMS(result, title.native); + for (auto& synonym : synonyms) { + ADD_TO_SYNONYMS(result, synonym); + } +#undef ADD_TO_SYNONYMS +#undef IN_VECTOR + return result; } void AnimeList::Add(Anime& anime) { @@ -131,6 +153,16 @@ name = l.name; } +AnimeList& AnimeList::operator=(const AnimeList& l) { + if (this != &l) { + for (unsigned long long i = 0; i < l.Size(); i++) { + this->anime_list.push_back(Anime(l[i])); + } + this->name = l.name; + } + return *this; +} + AnimeList::~AnimeList() { anime_list.clear(); anime_list.shrink_to_fit(); @@ -160,466 +192,3 @@ const Anime& AnimeList::operator[](std::size_t index) const { return anime_list.at(index); } - -/* ------------------------------------------------------------------------- */ - -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) - : QAbstractListModel(parent) - , list(*alist) { - return; -} - -int AnimeListWidgetModel::rowCount(const QModelIndex& parent) const { - return list.Size(); - (void)(parent); -} - -int AnimeListWidgetModel::columnCount(const QModelIndex& parent) const { - return NB_COLUMNS; - (void)(parent); -} - -QVariant AnimeListWidgetModel::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"); - case AL_PROGRESS: - return tr("Progress"); - case AL_EPISODES: - return tr("Episodes"); - case AL_TYPE: - return tr("Type"); - case AL_SCORE: - return tr("Score"); - case AL_SEASON: - return tr("Season"); - case AL_STARTED: - return tr("Date started"); - case AL_COMPLETED: - return tr("Date completed"); - case AL_NOTES: - return tr("Notes"); - case AL_AVG_SCORE: - return tr("Average score"); - case AL_UPDATED: - return tr("Last updated"); - default: - return {}; - } - } else if (role == Qt::TextAlignmentRole) { - switch (section) { - 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: - return QAbstractListModel::headerData(section, orientation, role); - } - } - return QAbstractListModel::headerData(section, orientation, role); -} - -Anime* AnimeListWidgetModel::GetAnimeFromIndex(const QModelIndex& index) { - return (index.isValid()) ? &(list[index.row()]) : nullptr; -} - -QVariant AnimeListWidgetModel::data(const QModelIndex& index, int role) const { - if (!index.isValid()) - return QVariant(); - 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 ""; - } - 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(); -} - -void AnimeListWidgetModel::UpdateAnime(Anime& anime) { - int i = list.GetAnimeIndex(anime); - emit dataChanged(index(i), index(i)); -} - -int AnimeListWidget::VisibleColumnsCount() const { - int count = 0; - - for (int i = 0, end = header()->count(); i < end; i++) - { - if (!isColumnHidden(i)) - count++; - } - - return count; -} - -void AnimeListWidget::SetColumnDefaults() { - setColumnHidden(AnimeListWidgetModel::AL_SEASON, false); - setColumnHidden(AnimeListWidgetModel::AL_TYPE, false); - setColumnHidden(AnimeListWidgetModel::AL_UPDATED, false); - 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); - setColumnHidden(AnimeListWidgetModel::AL_UPDATED, true); - setColumnHidden(AnimeListWidgetModel::AL_NOTES, true); -} - -void AnimeListWidget::DisplayColumnHeaderMenu() { - QMenu *menu = new QMenu(this); - menu->setAttribute(Qt::WA_DeleteOnClose); - menu->setTitle(tr("Column visibility")); - menu->setToolTipsVisible(true); - - for (int i = 0; i < AnimeListWidgetModel::NB_COLUMNS; i++) { - if (i == AnimeListWidgetModel::AL_TITLE) - continue; - const auto column_name = model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(); - QAction *action = menu->addAction(column_name, this, [this, i](const bool checked) { - if (!checked && (VisibleColumnsCount() <= 1)) - return; - - setColumnHidden(i, !checked); - - if (checked && (columnWidth(i) <= 5)) - resizeColumnToContents(i); - - // SaveSettings(); - }); - action->setCheckable(true); - action->setChecked(!isColumnHidden(i)); - } - - menu->addSeparator(); - QAction *resetAction = menu->addAction(tr("Reset to defaults"), this, [this]() { - for (int i = 0, count = header()->count(); i < count; ++i) - { - SetColumnDefaults(); - } - // SaveSettings(); - }); - menu->popup(QCursor::pos()); - (void)(resetAction); -} - -void AnimeListWidget::DisplayListMenu() { - 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; - } - - 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 QItemSelection selection = sort_model->mapSelectionToSource(selectionModel()->selection()); - if (!selection.indexes().first().isValid()) { - return; - } - - 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; - } - - InformationDialog* dialog = new InformationDialog(*anime, model, this); - - dialog->show(); - dialog->raise(); - dialog->activateWindow(); -} - -AnimeListWidget::AnimeListWidget(QWidget* parent, AnimeList* alist) - : QTreeView(parent) { - setItemDelegate(new AnimeListWidgetDelegate(this)); - model = new AnimeListWidgetModel(parent, alist); - sort_model = new AnimeListWidgetSortFilter(this); - sort_model->setSourceModel(model); - sort_model->setSortRole(Qt::UserRole); - setModel(sort_model); - setObjectName("listwidget"); - setStyleSheet("QTreeView#listwidget{border:0px;}"); - setUniformRowHeights(true); - setAllColumnsShowFocus(false); - setSortingEnabled(true); - setSelectionMode(QAbstractItemView::ExtendedSelection); - setItemsExpandable(false); - setRootIsDecorated(false); - setContextMenuPolicy(Qt::CustomContextMenu); - connect(this, &QAbstractItemView::doubleClicked, this, &AnimeListWidget::ItemDoubleClicked); - connect(this, &QWidget::customContextMenuRequested, this, &AnimeListWidget::DisplayListMenu); - - /* Enter & return keys */ - connect(new QShortcut(Qt::Key_Return, this, nullptr, nullptr, Qt::WidgetShortcut), - &QShortcut::activated, this, &AnimeListWidget::ItemDoubleClicked); - - connect(new QShortcut(Qt::Key_Enter, this, nullptr, nullptr, Qt::WidgetShortcut), - &QShortcut::activated, this, &AnimeListWidget::ItemDoubleClicked); - - header()->setStretchLastSection(false); - header()->setContextMenuPolicy(Qt::CustomContextMenu); - connect(header(), &QWidget::customContextMenuRequested, this, &AnimeListWidget::DisplayColumnHeaderMenu); - // if(!session.config.anime_list.columns) { - SetColumnDefaults(); - // } -} - -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::fromUtf8(list.name.c_str())); - } -} - -void AnimeListPage::SyncAnimeList() { - switch (session.config.service) { - case ANILIST: { - AniList anilist = AniList(); - session.config.anilist.user_id = anilist.GetUserId(session.config.anilist.username); - FreeAnimeList(); - anilist.UpdateAnimeList(&anime_lists, session.config.anilist.user_id); - break; - } - default: - break; - } -} - -void AnimeListPage::FreeAnimeList() { - for (auto& list : anime_lists) { - list.Clear(); - } - anime_lists.clear(); -} - -int AnimeListPage::GetTotalAnimeAmount() { - int total = 0; - for (auto& list : anime_lists) { - total += list.Size(); - } - return total; -} - -int AnimeListPage::GetTotalEpisodeAmount() { - /* FIXME: this also needs to take into account rewatches... */ - int total = 0; - for (auto& list : anime_lists) { - for (auto& anime : list) { - total += anime.progress; - } - } - return total; -} - -/* Returns the total watched amount in minutes. */ -int AnimeListPage::GetTotalWatchedAmount() { - int total = 0; - for (auto& list : anime_lists) { - for (auto& anime : list) { - total += anime.duration*anime.progress; - } - } - return total; -} - -/* Returns the total planned amount in minutes. - Note that we should probably limit progress to the - amount of episodes, as AniList will let you - set episode counts up to 32768. But that should - rather be handled elsewhere. */ -int AnimeListPage::GetTotalPlannedAmount() { - int total = 0; - for (auto& list : anime_lists) { - for (auto& anime : list) { - total += anime.duration*(anime.episodes-anime.progress); - } - } - return total; -} - -double AnimeListPage::GetAverageScore() { - double avg = 0; - int amt = 0; - for (auto& list : anime_lists) { - for (auto& anime : list) { - avg += anime.score; - if (anime.score != 0) - amt++; - } - } - return avg/amt; -} - -double AnimeListPage::GetScoreDeviation() { - double squares_sum = 0, avg = GetAverageScore(); - int amt = 0; - for (auto& list : anime_lists) { - for (auto& anime : list) { - if (anime.score != 0) { - squares_sum += std::pow((double)anime.score - avg, 2); - amt++; - } - } - } - return (amt > 0) ? std::sqrt(squares_sum / amt) : 0; -} - -#include "moc_anime.cpp"