Mercurial > minori
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 5:51ae25154b70 | 6:1d82f6e04d7d |
|---|---|
| 8 #include "anime.h" | 8 #include "anime.h" |
| 9 #include "date.h" | 9 #include "date.h" |
| 10 #include "time_utils.h" | 10 #include "time_utils.h" |
| 11 #include "information.h" | 11 #include "information.h" |
| 12 #include "ui_utils.h" | 12 #include "ui_utils.h" |
| 13 #include <iostream> | |
| 13 | 14 |
| 14 std::map<enum AnimeWatchingStatus, std::string> AnimeWatchingToStringMap = { | 15 std::map<enum AnimeWatchingStatus, std::string> AnimeWatchingToStringMap = { |
| 15 {CURRENT, "Watching"}, | 16 {CURRENT, "Watching"}, |
| 16 {PLANNING, "Planning"}, | 17 {PLANNING, "Planning"}, |
| 17 {COMPLETED, "Completed"}, | 18 {COMPLETED, "Completed"}, |
| 72 audience_score = a.audience_score; | 73 audience_score = a.audience_score; |
| 73 synopsis = a.synopsis; | 74 synopsis = a.synopsis; |
| 74 duration = a.duration; | 75 duration = a.duration; |
| 75 } | 76 } |
| 76 | 77 |
| 78 std::string Anime::GetUserPreferredTitle() { | |
| 79 if (title.english.empty()) | |
| 80 return title.romaji; | |
| 81 return title.english; | |
| 82 } | |
| 83 | |
| 77 void AnimeList::Add(Anime& anime) { | 84 void AnimeList::Add(Anime& anime) { |
| 78 if (anime_id_to_anime.contains(anime.id)) | 85 if (anime_id_to_anime.contains(anime.id)) |
| 79 return; | 86 return; |
| 80 anime_list.push_back(anime); | 87 anime_list.push_back(anime); |
| 81 anime_id_to_anime.emplace(anime.id, &anime); | 88 anime_id_to_anime.emplace(anime.id, &anime); |
| 153 const Anime& AnimeList::operator[](std::size_t index) const { | 160 const Anime& AnimeList::operator[](std::size_t index) const { |
| 154 return anime_list.at(index); | 161 return anime_list.at(index); |
| 155 } | 162 } |
| 156 | 163 |
| 157 /* ------------------------------------------------------------------------- */ | 164 /* ------------------------------------------------------------------------- */ |
| 165 | |
| 166 AnimeListWidgetDelegate::AnimeListWidgetDelegate(QObject* parent) | |
| 167 : QStyledItemDelegate (parent) { | |
| 168 } | |
| 169 | |
| 170 QWidget *AnimeListWidgetDelegate::createEditor(QWidget *, const QStyleOptionViewItem &, const QModelIndex &) const | |
| 171 { | |
| 172 // LOL | |
| 173 return nullptr; | |
| 174 } | |
| 175 | |
| 176 void AnimeListWidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const | |
| 177 { | |
| 178 switch (index.column()) { | |
| 179 case AnimeListWidgetModel::AL_PROGRESS: { | |
| 180 const int progress = static_cast<int>(index.data(Qt::UserRole).toReal()); | |
| 181 const int episodes = static_cast<int>(index.siblingAtColumn(AnimeListWidgetModel::AL_EPISODES).data(Qt::UserRole).toReal()); | |
| 182 | |
| 183 QStyleOptionViewItem customOption (option); | |
| 184 customOption.state.setFlag(QStyle::State_Enabled, true); | |
| 185 | |
| 186 progress_bar.paint(painter, customOption, index.data().toString(), progress, episodes); | |
| 187 break; | |
| 188 } | |
| 189 default: | |
| 190 QStyledItemDelegate::paint(painter, option, index); | |
| 191 break; | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 AnimeListWidgetSortFilter::AnimeListWidgetSortFilter(QObject *parent) | |
| 196 : QSortFilterProxyModel(parent) { | |
| 197 } | |
| 198 | |
| 199 bool AnimeListWidgetSortFilter::lessThan(const QModelIndex &l, | |
| 200 const QModelIndex &r) const { | |
| 201 QVariant left = sourceModel()->data(l, sortRole()); | |
| 202 QVariant right = sourceModel()->data(r, sortRole()); | |
| 203 | |
| 204 switch (left.userType()) { | |
| 205 case QMetaType::Int: | |
| 206 case QMetaType::UInt: | |
| 207 case QMetaType::LongLong: | |
| 208 case QMetaType::ULongLong: | |
| 209 return left.toInt() < right.toInt(); | |
| 210 case QMetaType::QDate: | |
| 211 return left.toDate() < right.toDate(); | |
| 212 case QMetaType::QString: | |
| 213 default: | |
| 214 return left.toString() < right.toString(); | |
| 215 } | |
| 216 } | |
| 158 | 217 |
| 159 /* Thank you qBittorrent for having a great example of a | 218 /* Thank you qBittorrent for having a great example of a |
| 160 widget model. */ | 219 widget model. */ |
| 161 AnimeListWidgetModel::AnimeListWidgetModel (QWidget* parent, AnimeList* alist) | 220 AnimeListWidgetModel::AnimeListWidgetModel (QWidget* parent, AnimeList* alist) |
| 162 : QAbstractListModel(parent) | 221 : QAbstractListModel(parent) |
| 179 switch (section) { | 238 switch (section) { |
| 180 case AL_TITLE: | 239 case AL_TITLE: |
| 181 return tr("Anime title"); | 240 return tr("Anime title"); |
| 182 case AL_PROGRESS: | 241 case AL_PROGRESS: |
| 183 return tr("Progress"); | 242 return tr("Progress"); |
| 243 case AL_EPISODES: | |
| 244 return tr("Episodes"); | |
| 184 case AL_TYPE: | 245 case AL_TYPE: |
| 185 return tr("Type"); | 246 return tr("Type"); |
| 186 case AL_SCORE: | 247 case AL_SCORE: |
| 187 return tr("Score"); | 248 return tr("Score"); |
| 188 case AL_SEASON: | 249 case AL_SEASON: |
| 204 switch (section) { | 265 switch (section) { |
| 205 case AL_TITLE: | 266 case AL_TITLE: |
| 206 case AL_NOTES: | 267 case AL_NOTES: |
| 207 return QVariant(Qt::AlignLeft | Qt::AlignVCenter); | 268 return QVariant(Qt::AlignLeft | Qt::AlignVCenter); |
| 208 case AL_PROGRESS: | 269 case AL_PROGRESS: |
| 270 case AL_EPISODES: | |
| 209 case AL_TYPE: | 271 case AL_TYPE: |
| 210 case AL_SCORE: | 272 case AL_SCORE: |
| 211 case AL_AVG_SCORE: | 273 case AL_AVG_SCORE: |
| 212 return QVariant(Qt::AlignCenter | Qt::AlignVCenter); | 274 return QVariant(Qt::AlignCenter | Qt::AlignVCenter); |
| 213 case AL_SEASON: | 275 case AL_SEASON: |
| 227 } | 289 } |
| 228 | 290 |
| 229 QVariant AnimeListWidgetModel::data(const QModelIndex& index, int role) const { | 291 QVariant AnimeListWidgetModel::data(const QModelIndex& index, int role) const { |
| 230 if (!index.isValid()) | 292 if (!index.isValid()) |
| 231 return QVariant(); | 293 return QVariant(); |
| 232 if (role == Qt::DisplayRole) { | 294 switch (role) { |
| 233 switch (index.column()) { | 295 case Qt::DisplayRole: |
| 234 case AL_TITLE: | 296 switch (index.column()) { |
| 235 return QString::fromWCharArray(list[index.row()].title.english.c_str()); | 297 case AL_TITLE: |
| 236 case AL_PROGRESS: | 298 return QString::fromUtf8(list[index.row()].GetUserPreferredTitle().c_str()); |
| 237 return list[index.row()].progress; | 299 case AL_PROGRESS: |
| 238 case AL_SCORE: | 300 return QString::number(list[index.row()].progress) + "/" + QString::number(list[index.row()].episodes); |
| 239 return list[index.row()].score; | 301 case AL_EPISODES: |
| 240 case AL_TYPE: | 302 return list[index.row()].episodes; |
| 241 return QString::fromStdString(AnimeFormatToStringMap[list[index.row()].type]); | 303 case AL_SCORE: |
| 242 case AL_SEASON: | 304 return list[index.row()].score; |
| 243 return QString::fromStdString(AnimeSeasonToStringMap[list[index.row()].season]) + " " + QString::number(list[index.row()].air_date.GetYear()); | 305 case AL_TYPE: |
| 244 case AL_AVG_SCORE: | 306 return QString::fromStdString(AnimeFormatToStringMap[list[index.row()].type]); |
| 245 return list[index.row()].audience_score; | 307 case AL_SEASON: |
| 246 case AL_STARTED: | 308 return QString::fromStdString(AnimeSeasonToStringMap[list[index.row()].season]) + " " + QString::number(list[index.row()].air_date.GetYear()); |
| 247 return list[index.row()].started.GetAsQDate(); | 309 case AL_AVG_SCORE: |
| 248 case AL_COMPLETED: | 310 return QString::number(list[index.row()].audience_score) + "%"; |
| 249 return list[index.row()].completed.GetAsQDate(); | 311 case AL_STARTED: |
| 250 case AL_UPDATED: { | 312 return list[index.row()].started.GetAsQDate(); |
| 251 if (list[index.row()].updated == 0) | 313 case AL_COMPLETED: |
| 252 return QString("-"); | 314 return list[index.row()].completed.GetAsQDate(); |
| 253 Time::Duration duration(Time::GetSystemTime() - list[index.row()].updated); | 315 case AL_UPDATED: { |
| 254 return QString::fromStdString(duration.AsRelativeString()); | 316 if (list[index.row()].updated == 0) |
| 317 return QString("-"); | |
| 318 Time::Duration duration(Time::GetSystemTime() - list[index.row()].updated); | |
| 319 return QString::fromUtf8(duration.AsRelativeString().c_str()); | |
| 320 } | |
| 321 case AL_NOTES: | |
| 322 return QString::fromUtf8(list[index.row()].notes.c_str()); | |
| 323 default: | |
| 324 return ""; | |
| 255 } | 325 } |
| 256 case AL_NOTES: | 326 break; |
| 257 return QString::fromWCharArray(list[index.row()].notes.c_str()); | 327 case Qt::UserRole: |
| 258 default: | 328 switch (index.column()) { |
| 259 return ""; | 329 case AL_PROGRESS: |
| 260 } | 330 return list[index.row()].progress; |
| 261 } else if (role == Qt::TextAlignmentRole) { | 331 case AL_TYPE: |
| 262 switch (index.column()) { | 332 return list[index.row()].type; |
| 263 case AL_TITLE: | 333 case AL_SEASON: |
| 264 case AL_NOTES: | 334 return list[index.row()].air_date.GetAsQDate(); |
| 265 return QVariant(Qt::AlignLeft | Qt::AlignVCenter); | 335 case AL_AVG_SCORE: |
| 266 case AL_PROGRESS: | 336 return list[index.row()].audience_score; |
| 267 case AL_TYPE: | 337 case AL_UPDATED: |
| 268 case AL_SCORE: | 338 return list[index.row()].updated; |
| 269 case AL_AVG_SCORE: | 339 default: |
| 270 return QVariant(Qt::AlignCenter | Qt::AlignVCenter); | 340 return data(index, Qt::DisplayRole); |
| 271 case AL_SEASON: | 341 } |
| 272 case AL_STARTED: | 342 break; |
| 273 case AL_COMPLETED: | 343 case Qt::TextAlignmentRole: |
| 274 case AL_UPDATED: | 344 switch (index.column()) { |
| 275 return QVariant(Qt::AlignRight | Qt::AlignVCenter); | 345 case AL_TITLE: |
| 276 default: | 346 case AL_NOTES: |
| 277 break; | 347 return QVariant(Qt::AlignLeft | Qt::AlignVCenter); |
| 278 } | 348 case AL_PROGRESS: |
| 349 case AL_EPISODES: | |
| 350 case AL_TYPE: | |
| 351 case AL_SCORE: | |
| 352 case AL_AVG_SCORE: | |
| 353 return QVariant(Qt::AlignCenter | Qt::AlignVCenter); | |
| 354 case AL_SEASON: | |
| 355 case AL_STARTED: | |
| 356 case AL_COMPLETED: | |
| 357 case AL_UPDATED: | |
| 358 return QVariant(Qt::AlignRight | Qt::AlignVCenter); | |
| 359 default: | |
| 360 break; | |
| 361 } | |
| 362 break; | |
| 279 } | 363 } |
| 280 return QVariant(); | 364 return QVariant(); |
| 281 } | 365 } |
| 282 | 366 |
| 283 void AnimeListWidgetModel::UpdateAnime(Anime& anime) { | 367 void AnimeListWidgetModel::UpdateAnime(Anime& anime) { |
| 284 int i = list.GetAnimeIndex(anime); | 368 int i = list.GetAnimeIndex(anime); |
| 285 emit dataChanged(index(i), index(i)); | 369 emit dataChanged(index(i), index(i)); |
| 286 } | 370 } |
| 287 | |
| 288 /* Most of this stuff is const and/or should be edited in the Information dialog | |
| 289 | |
| 290 bool AnimeListWidgetModel::setData(const QModelIndex &index, const QVariant &value, int role) { | |
| 291 if (!index.isValid() || role != Qt::DisplayRole) | |
| 292 return false; | |
| 293 | |
| 294 Anime* const anime = &list[index.row()]; | |
| 295 | |
| 296 switch (index.column()) { | |
| 297 case AL_TITLE: | |
| 298 break; | |
| 299 case AL_CATEGORY: | |
| 300 break; | |
| 301 default: | |
| 302 return false; | |
| 303 } | |
| 304 | |
| 305 return true; | |
| 306 } | |
| 307 */ | |
| 308 | 371 |
| 309 int AnimeListWidget::VisibleColumnsCount() const { | 372 int AnimeListWidget::VisibleColumnsCount() const { |
| 310 int count = 0; | 373 int count = 0; |
| 311 | 374 |
| 312 for (int i = 0, end = header()->count(); i < end; i++) | 375 for (int i = 0, end = header()->count(); i < end; i++) |
| 323 setColumnHidden(AnimeListWidgetModel::AL_TYPE, false); | 386 setColumnHidden(AnimeListWidgetModel::AL_TYPE, false); |
| 324 setColumnHidden(AnimeListWidgetModel::AL_UPDATED, false); | 387 setColumnHidden(AnimeListWidgetModel::AL_UPDATED, false); |
| 325 setColumnHidden(AnimeListWidgetModel::AL_PROGRESS, false); | 388 setColumnHidden(AnimeListWidgetModel::AL_PROGRESS, false); |
| 326 setColumnHidden(AnimeListWidgetModel::AL_SCORE, false); | 389 setColumnHidden(AnimeListWidgetModel::AL_SCORE, false); |
| 327 setColumnHidden(AnimeListWidgetModel::AL_TITLE, false); | 390 setColumnHidden(AnimeListWidgetModel::AL_TITLE, false); |
| 391 setColumnHidden(AnimeListWidgetModel::AL_EPISODES, true); | |
| 328 setColumnHidden(AnimeListWidgetModel::AL_AVG_SCORE, true); | 392 setColumnHidden(AnimeListWidgetModel::AL_AVG_SCORE, true); |
| 329 setColumnHidden(AnimeListWidgetModel::AL_STARTED, true); | 393 setColumnHidden(AnimeListWidgetModel::AL_STARTED, true); |
| 330 setColumnHidden(AnimeListWidgetModel::AL_COMPLETED, true); | 394 setColumnHidden(AnimeListWidgetModel::AL_COMPLETED, true); |
| 331 setColumnHidden(AnimeListWidgetModel::AL_UPDATED, true); | 395 setColumnHidden(AnimeListWidgetModel::AL_UPDATED, true); |
| 332 setColumnHidden(AnimeListWidgetModel::AL_NOTES, true); | 396 setColumnHidden(AnimeListWidgetModel::AL_NOTES, true); |
| 368 menu->popup(QCursor::pos()); | 432 menu->popup(QCursor::pos()); |
| 369 (void)(resetAction); | 433 (void)(resetAction); |
| 370 } | 434 } |
| 371 | 435 |
| 372 void AnimeListWidget::DisplayListMenu() { | 436 void AnimeListWidget::DisplayListMenu() { |
| 437 QMenu *menu = new QMenu(this); | |
| 438 menu->setAttribute(Qt::WA_DeleteOnClose); | |
| 439 menu->setTitle(tr("Column visibility")); | |
| 440 menu->setToolTipsVisible(true); | |
| 441 | |
| 442 const QItemSelection selection = sort_model->mapSelectionToSource(selectionModel()->selection()); | |
| 443 if (!selection.indexes().first().isValid()) { | |
| 444 return; | |
| 445 } | |
| 446 | |
| 447 QAction* action = menu->addAction("Information", [this, selection]{ | |
| 448 const QModelIndex index = model->index(selection.indexes().first().row()); | |
| 449 Anime* anime = model->GetAnimeFromIndex(index); | |
| 450 if (!anime) { | |
| 451 return; | |
| 452 } | |
| 453 | |
| 454 InformationDialog* dialog = new InformationDialog(*anime, model, this); | |
| 455 | |
| 456 dialog->show(); | |
| 457 dialog->raise(); | |
| 458 dialog->activateWindow(); | |
| 459 }); | |
| 460 menu->popup(QCursor::pos()); | |
| 461 } | |
| 462 | |
| 463 void AnimeListWidget::ItemDoubleClicked() { | |
| 373 /* throw out any other garbage */ | 464 /* throw out any other garbage */ |
| 374 const QModelIndexList selected_items = selectionModel()->selectedRows(); | 465 const QItemSelection selection = sort_model->mapSelectionToSource(selectionModel()->selection()); |
| 375 if (selected_items.size() != 1 || !selected_items.first().isValid()) { | 466 if (!selection.indexes().first().isValid()) { |
| 376 return; | 467 return; |
| 377 } | 468 } |
| 378 | 469 |
| 379 const QModelIndex index = model->index(selected_items.first().row()); | 470 const QModelIndex index = model->index(selection.indexes().first().row()); |
| 471 const QString title = index.siblingAtColumn(AnimeListWidgetModel::AL_TITLE).data(Qt::UserRole).toString(); | |
| 472 QMessageBox box; | |
| 473 box.setText(QString::number(title.size())); | |
| 474 box.exec(); | |
| 380 Anime* anime = model->GetAnimeFromIndex(index); | 475 Anime* anime = model->GetAnimeFromIndex(index); |
| 381 if (!anime) { | 476 if (!anime) { |
| 382 return; | 477 return; |
| 383 } | 478 } |
| 384 | 479 |
| 385 } | |
| 386 | |
| 387 void AnimeListWidget::ItemDoubleClicked() { | |
| 388 /* throw out any other garbage */ | |
| 389 const QModelIndexList selected_items = selectionModel()->selectedRows(); | |
| 390 if (selected_items.size() != 1 || !selected_items.first().isValid()) { | |
| 391 return; | |
| 392 } | |
| 393 | |
| 394 /* TODO: after we implement our sort model, we have to use mapToSource here... */ | |
| 395 const QModelIndex index = model->index(selected_items.first().row()); | |
| 396 Anime* anime = model->GetAnimeFromIndex(index); | |
| 397 if (!anime) { | |
| 398 return; | |
| 399 } | |
| 400 | |
| 401 InformationDialog* dialog = new InformationDialog(*anime, model, this); | 480 InformationDialog* dialog = new InformationDialog(*anime, model, this); |
| 402 | 481 |
| 403 dialog->show(); | 482 dialog->show(); |
| 404 dialog->raise(); | 483 dialog->raise(); |
| 405 dialog->activateWindow(); | 484 dialog->activateWindow(); |
| 406 } | 485 } |
| 407 | 486 |
| 408 AnimeListWidget::AnimeListWidget(QWidget* parent, AnimeList* alist) | 487 AnimeListWidget::AnimeListWidget(QWidget* parent, AnimeList* alist) |
| 409 : QTreeView(parent) { | 488 : QTreeView(parent) { |
| 489 setItemDelegate(new AnimeListWidgetDelegate(this)); | |
| 410 model = new AnimeListWidgetModel(parent, alist); | 490 model = new AnimeListWidgetModel(parent, alist); |
| 411 setModel(model); | 491 sort_model = new AnimeListWidgetSortFilter(this); |
| 492 sort_model->setSourceModel(model); | |
| 493 sort_model->setSortRole(Qt::UserRole); | |
| 494 setModel(sort_model); | |
| 412 setObjectName("listwidget"); | 495 setObjectName("listwidget"); |
| 413 setStyleSheet("QTreeView#listwidget{border-top:0px;}"); | 496 setStyleSheet("QTreeView#listwidget{border:0px;}"); |
| 414 setUniformRowHeights(true); | 497 setUniformRowHeights(true); |
| 415 setAllColumnsShowFocus(false); | 498 setAllColumnsShowFocus(false); |
| 416 setSortingEnabled(true); | 499 setSortingEnabled(true); |
| 417 setSelectionMode(QAbstractItemView::ExtendedSelection); | 500 setSelectionMode(QAbstractItemView::ExtendedSelection); |
| 418 setItemsExpandable(false); | 501 setItemsExpandable(false); |
| 436 // } | 519 // } |
| 437 } | 520 } |
| 438 | 521 |
| 439 AnimeListPage::AnimeListPage(QWidget* parent) : QTabWidget (parent) { | 522 AnimeListPage::AnimeListPage(QWidget* parent) : QTabWidget (parent) { |
| 440 setDocumentMode(false); | 523 setDocumentMode(false); |
| 524 setObjectName("animepage"); | |
| 525 //setStyleSheet("QTabWidget#animepage{border-bottom:0px;border-left:0px;border-right:0px;}"); | |
| 441 SyncAnimeList(); | 526 SyncAnimeList(); |
| 442 for (AnimeList& list : anime_lists) { | 527 for (AnimeList& list : anime_lists) { |
| 443 addTab(new AnimeListWidget(this, &list), QString::fromWCharArray(list.name.c_str())); | 528 addTab(new AnimeListWidget(this, &list), QString::fromUtf8(list.name.c_str())); |
| 444 } | 529 } |
| 445 } | 530 } |
| 446 | 531 |
| 447 void AnimeListPage::SyncAnimeList() { | 532 void AnimeListPage::SyncAnimeList() { |
| 448 switch (session.config.service) { | 533 switch (session.config.service) { |
| 449 case ANILIST: { | 534 case ANILIST: { |
| 450 AniList anilist = AniList(); | 535 AniList anilist = AniList(); |
| 451 anilist.Authorize(); | |
| 452 session.config.anilist.user_id = anilist.GetUserId(session.config.anilist.username); | 536 session.config.anilist.user_id = anilist.GetUserId(session.config.anilist.username); |
| 453 FreeAnimeList(); | 537 FreeAnimeList(); |
| 454 anilist.UpdateAnimeList(&anime_lists, session.config.anilist.user_id); | 538 anilist.UpdateAnimeList(&anime_lists, session.config.anilist.user_id); |
| 455 break; | 539 break; |
| 456 } | 540 } |
| 458 break; | 542 break; |
| 459 } | 543 } |
| 460 } | 544 } |
| 461 | 545 |
| 462 void AnimeListPage::FreeAnimeList() { | 546 void AnimeListPage::FreeAnimeList() { |
| 463 if (anime_lists.size() > 0) { | 547 for (auto& list : anime_lists) { |
| 464 /* FIXME: we may not need this, but to prevent memleaks | 548 list.Clear(); |
| 465 we should keep it until we're sure we don't */ | 549 } |
| 466 for (auto& list : anime_lists) { | 550 anime_lists.clear(); |
| 467 list.Clear(); | |
| 468 } | |
| 469 anime_lists.clear(); | |
| 470 } | |
| 471 } | 551 } |
| 472 | 552 |
| 473 int AnimeListPage::GetTotalAnimeAmount() { | 553 int AnimeListPage::GetTotalAnimeAmount() { |
| 474 int total = 0; | 554 int total = 0; |
| 475 for (auto& list : anime_lists) { | 555 for (auto& list : anime_lists) { |
