Mercurial > minori
comparison src/gui/pages/anime_list.cc @ 366:886f66775f31
animone: add preliminary AT-SPI stuff
anime_list: finish the regular singular right click menu
| author | Paper <paper@tflc.us> |
|---|---|
| date | Sun, 17 Nov 2024 19:56:01 -0500 |
| parents | b5d6c27c308f |
| children | ea3a74ed2ef9 |
comparison
equal
deleted
inserted
replaced
| 365:f81bed4e04ac | 366:886f66775f31 |
|---|---|
| 12 #include "core/anime.h" | 12 #include "core/anime.h" |
| 13 #include "core/anime_db.h" | 13 #include "core/anime_db.h" |
| 14 #include "core/session.h" | 14 #include "core/session.h" |
| 15 #include "core/strings.h" | 15 #include "core/strings.h" |
| 16 #include "core/time.h" | 16 #include "core/time.h" |
| 17 #include "library/library.h" | |
| 17 #include "gui/dialog/information.h" | 18 #include "gui/dialog/information.h" |
| 18 #include "gui/translate/anime.h" | 19 #include "gui/translate/anime.h" |
| 19 #include "services/services.h" | 20 #include "services/services.h" |
| 20 | 21 |
| 21 #include <QDate> | 22 #include <QDate> |
| 28 #include <QStylePainter> | 29 #include <QStylePainter> |
| 29 #include <QStyledItemDelegate> | 30 #include <QStyledItemDelegate> |
| 30 #include <QThreadPool> | 31 #include <QThreadPool> |
| 31 #include <QRunnable> | 32 #include <QRunnable> |
| 32 #include <QTreeView> | 33 #include <QTreeView> |
| 33 | 34 #include <QDesktopServices> |
| 34 #include <set> | 35 #include <QUrl> |
| 36 | |
| 37 #include <iostream> | |
| 38 #include <vector> | |
| 35 | 39 |
| 36 AnimeListPageUpdateEntryThread::AnimeListPageUpdateEntryThread(QObject* parent) : QThread(parent) {} | 40 AnimeListPageUpdateEntryThread::AnimeListPageUpdateEntryThread(QObject* parent) : QThread(parent) {} |
| 37 | 41 |
| 38 void AnimeListPageUpdateEntryThread::AddToQueue(int id) { | 42 void AnimeListPageUpdateEntryThread::AddToQueue(int id) { |
| 39 const std::lock_guard<std::mutex> guard(queue_mutex_); | 43 const std::lock_guard<std::mutex> guard(queue_mutex_); |
| 270 tree_view->setColumnHidden(i, !checked); | 274 tree_view->setColumnHidden(i, !checked); |
| 271 | 275 |
| 272 if (checked && (tree_view->columnWidth(i) <= 5)) | 276 if (checked && (tree_view->columnWidth(i) <= 5)) |
| 273 tree_view->resizeColumnToContents(i); | 277 tree_view->resizeColumnToContents(i); |
| 274 | 278 |
| 275 // SaveSettings(); | 279 // FIXME save the state of this |
| 276 }); | 280 }); |
| 277 | 281 |
| 278 action->setCheckable(true); | 282 action->setCheckable(true); |
| 279 action->setChecked(!tree_view->isColumnHidden(i)); | 283 action->setChecked(!tree_view->isColumnHidden(i)); |
| 280 } | 284 } |
| 288 }); | 292 }); |
| 289 menu->popup(QCursor::pos()); | 293 menu->popup(QCursor::pos()); |
| 290 } | 294 } |
| 291 | 295 |
| 292 void AnimeListPage::DisplayListMenu() { | 296 void AnimeListPage::DisplayListMenu() { |
| 293 QMenu* menu = new QMenu(this); | 297 QMenu *const menu = new QMenu(this); |
| 294 menu->setAttribute(Qt::WA_DeleteOnClose); | 298 menu->setAttribute(Qt::WA_DeleteOnClose); |
| 295 menu->setToolTipsVisible(true); | 299 menu->setToolTipsVisible(true); |
| 296 | 300 |
| 297 AnimeListPageModel* source_model = | 301 AnimeListPageModel* source_model = |
| 298 reinterpret_cast<AnimeListPageModel*>(sort_models[tab_bar->currentIndex()]->sourceModel()); | 302 reinterpret_cast<AnimeListPageModel*>(sort_models[tab_bar->currentIndex()]->sourceModel()); |
| 301 | 305 |
| 302 std::set<Anime::Anime*> animes; | 306 std::set<Anime::Anime*> animes; |
| 303 for (const auto& index : selection.indexes()) { | 307 for (const auto& index : selection.indexes()) { |
| 304 if (!index.isValid()) | 308 if (!index.isValid()) |
| 305 continue; | 309 continue; |
| 306 Anime::Anime* anime = source_model->GetAnimeFromIndex(index); | 310 |
| 311 Anime::Anime *const anime = source_model->GetAnimeFromIndex(index); | |
| 307 if (!anime) | 312 if (!anime) |
| 308 continue; | 313 continue; |
| 314 | |
| 309 animes.insert(&Anime::db.items[anime->GetId()]); | 315 animes.insert(&Anime::db.items[anime->GetId()]); |
| 310 } | 316 } |
| 311 | 317 |
| 312 menu->addAction(tr("Information"), [this, animes] { | 318 if (animes.size() > 1) { |
| 313 for (auto& anime : animes) { | 319 // menu in Taiga: |
| 320 // | |
| 321 // Set date started -> | |
| 322 // Clear | |
| 323 // Set to date started airing | |
| 324 // Set date completed -> | |
| 325 // Clear | |
| 326 // Set to date finished airing | |
| 327 // Set to last updated | |
| 328 // Set episode... | |
| 329 // Set score -> | |
| 330 // 0 | |
| 331 // 10 | |
| 332 // ... | |
| 333 // 100 | |
| 334 // Set status -> | |
| 335 // Currently watching | |
| 336 // ... | |
| 337 // Plan to watch | |
| 338 // Set notes... | |
| 339 // ---------------- | |
| 340 // Invert selection | |
| 341 // ---------------- | |
| 342 // Delete from list... <Del> | |
| 343 } else if (animes.size() > 0) { | |
| 344 // menu in Taiga: | |
| 345 // | |
| 346 // Information | |
| 347 // Search -> | |
| 348 // AniDB | |
| 349 // AniList | |
| 350 // Anime News Network | |
| 351 // Kitsu | |
| 352 // MyAnimeList | |
| 353 // Reddit | |
| 354 // Wikipedia | |
| 355 // YouTube | |
| 356 // ---------------- | |
| 357 // Custom RSS feed | |
| 358 // Nyaa.si | |
| 359 // ---------------- | |
| 360 // Edit | |
| 361 // Delete from list... <Del> | |
| 362 // ---------------- | |
| 363 // Open folder <Ctrl+O> | |
| 364 // Scan available episodes <F5> | |
| 365 // ---------------- | |
| 366 // Play episode -> | |
| 367 // grid of episodes (dunno how to implement this) | |
| 368 // Play last episode (#<episode>) | |
| 369 // Play next episode (#<episode>) <Ctrl+N> | |
| 370 // Play random episode <Ctrl+R> (why?) | |
| 371 | |
| 372 Anime::Anime *anime = *animes.begin(); | |
| 373 | |
| 374 menu->addAction(tr("Information"), [this, anime] { | |
| 314 InformationDialog* dialog = new InformationDialog( | 375 InformationDialog* dialog = new InformationDialog( |
| 315 anime, [this](Anime::Anime* anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, this); | 376 anime, [this](Anime::Anime* anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, this); |
| 316 | 377 |
| 317 dialog->show(); | 378 dialog->show(); |
| 318 dialog->raise(); | 379 dialog->raise(); |
| 319 dialog->activateWindow(); | 380 dialog->activateWindow(); |
| 320 connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater); | 381 connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater); |
| 321 } | 382 }); |
| 322 }); | 383 |
| 323 menu->addSeparator(); | 384 menu->addSeparator(); |
| 324 menu->addAction(tr("Edit"), [this, animes] { | 385 |
| 325 for (auto& anime : animes) { | 386 menu->addAction(tr("Edit"), [this, anime] { |
| 326 InformationDialog* dialog = new InformationDialog( | 387 InformationDialog* dialog = new InformationDialog( |
| 327 anime, [this](Anime::Anime* anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MY_LIST, this); | 388 anime, [this](Anime::Anime* anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MY_LIST, this); |
| 328 | 389 |
| 329 dialog->show(); | 390 dialog->show(); |
| 330 dialog->raise(); | 391 dialog->raise(); |
| 331 dialog->activateWindow(); | 392 dialog->activateWindow(); |
| 332 connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater); | 393 connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater); |
| 394 }); | |
| 395 menu->addAction(tr("Delete from list..."), [this, anime] { | |
| 396 RemoveAnime(anime->GetId()); | |
| 397 }, QKeySequence(QKeySequence::Delete)); | |
| 398 | |
| 399 menu->addSeparator(); | |
| 400 | |
| 401 menu->addAction(tr("Open folder"), [this, anime] { | |
| 402 std::optional<std::filesystem::path> path = Library::db.GetAnimeFolder(anime->GetId()); | |
| 403 if (!path) // ... | |
| 404 return; | |
| 405 | |
| 406 QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(path.value().u8string()))); | |
| 407 }); | |
| 408 menu->addAction(tr("Scan available episodes"), [this, anime] { | |
| 409 Library::db.Refresh(anime->GetId()); | |
| 410 }); | |
| 411 | |
| 412 menu->addSeparator(); | |
| 413 | |
| 414 { | |
| 415 QMenu *submenu = menu->addMenu(tr("Play episode")); | |
| 416 | |
| 417 // this submenu actually uses win32 API magic to | |
| 418 // make a *grid* of episodes (weird!) | |
| 419 | |
| 420 (void)submenu; | |
| 333 } | 421 } |
| 334 }); | 422 |
| 335 menu->addAction(tr("Delete from list..."), [this, animes] { | 423 const int progress = anime->GetUserProgress(); |
| 336 for (auto& anime : animes) { | 424 const int episodes = anime->GetEpisodes(); |
| 337 RemoveAnime(anime->GetId()); | 425 |
| 426 // I think this is right? | |
| 427 if (progress > 0) { | |
| 428 menu->addAction(tr("Play last episode (#%1)").arg(progress), [this, anime, progress] { | |
| 429 const int id = anime->GetId(); | |
| 430 | |
| 431 if (Library::db.items.find(id) == Library::db.items.end() | |
| 432 || Library::db.items[id].find(progress) == Library::db.items[id].end()) | |
| 433 return; | |
| 434 | |
| 435 QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress].u8string()))); | |
| 436 }); | |
| 338 } | 437 } |
| 339 }); | 438 |
| 340 menu->popup(QCursor::pos()); | 439 if (progress < episodes) { |
| 440 menu->addAction(tr("Play next episode (#%1)").arg(progress + 1), [this, anime, progress] { | |
| 441 const int id = anime->GetId(); | |
| 442 | |
| 443 if (Library::db.items.find(id) == Library::db.items.end() | |
| 444 || Library::db.items[id].find(progress + 1) == Library::db.items[id].end()) | |
| 445 return; | |
| 446 | |
| 447 QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress + 1].u8string()))); | |
| 448 }, QKeySequence(Qt::CTRL | Qt::Key_N)); | |
| 449 } | |
| 450 | |
| 451 menu->addAction(tr("Play random episode"), [this, anime, episodes] { | |
| 452 const int id = anime->GetId(); | |
| 453 | |
| 454 std::uniform_int_distribution<int> distrib(1, episodes); | |
| 455 const int episode = distrib(session.gen); | |
| 456 | |
| 457 if (Library::db.items.find(id) == Library::db.items.end() | |
| 458 || Library::db.items[id].find(episode) == Library::db.items[id].end()) | |
| 459 return; | |
| 460 | |
| 461 QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][episode].u8string()))); | |
| 462 }, QKeySequence(Qt::CTRL | Qt::Key_R)); | |
| 463 | |
| 464 menu->popup(QCursor::pos()); | |
| 465 } else { | |
| 466 // Where are we now? | |
| 467 } | |
| 341 } | 468 } |
| 342 | 469 |
| 343 void AnimeListPage::ItemDoubleClicked() { | 470 void AnimeListPage::ItemDoubleClicked() { |
| 344 /* throw out any other garbage */ | 471 /* throw out any other garbage */ |
| 345 const QItemSelection selection = | 472 const QItemSelection selection = |
