comparison 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
comparison
equal deleted inserted replaced
369:47c9f8502269 370:ea3a74ed2ef9
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"
18 #include "gui/dialog/information.h" 17 #include "gui/dialog/information.h"
19 #include "gui/translate/anime.h" 18 #include "gui/translate/anime.h"
19 #include "library/library.h"
20 #include "services/services.h" 20 #include "services/services.h"
21 21
22 #include <QDate> 22 #include <QDate>
23 #include <QDebug> 23 #include <QDebug>
24 #include <QDesktopServices>
24 #include <QHBoxLayout> 25 #include <QHBoxLayout>
25 #include <QHeaderView> 26 #include <QHeaderView>
26 #include <QMenu> 27 #include <QMenu>
27 #include <QProgressBar> 28 #include <QProgressBar>
29 #include <QRunnable>
28 #include <QShortcut> 30 #include <QShortcut>
29 #include <QStylePainter> 31 #include <QStylePainter>
30 #include <QStyledItemDelegate> 32 #include <QStyledItemDelegate>
31 #include <QThreadPool> 33 #include <QThreadPool>
32 #include <QRunnable>
33 #include <QTreeView> 34 #include <QTreeView>
34 #include <QDesktopServices>
35 #include <QUrl> 35 #include <QUrl>
36 36
37 #include <iostream> 37 #include <iostream>
38 #include <vector> 38 #include <vector>
39 39
40 AnimeListPageUpdateEntryThread::AnimeListPageUpdateEntryThread(QObject* parent) : QThread(parent) {} 40 AnimeListPageUpdateEntryThread::AnimeListPageUpdateEntryThread(QObject *parent) : QThread(parent)
41 41 {
42 void AnimeListPageUpdateEntryThread::AddToQueue(int id) { 42 }
43
44 void AnimeListPageUpdateEntryThread::AddToQueue(int id)
45 {
43 const std::lock_guard<std::mutex> guard(queue_mutex_); 46 const std::lock_guard<std::mutex> guard(queue_mutex_);
44 queue_.push(id); 47 queue_.push(id);
45 } 48 }
46 49
47 /* processes the queue... */ 50 /* processes the queue... */
48 void AnimeListPageUpdateEntryThread::run() { 51 void AnimeListPageUpdateEntryThread::run()
52 {
49 queue_mutex_.lock(); 53 queue_mutex_.lock();
50 while (!queue_.empty() && !isInterruptionRequested()) { 54 while (!queue_.empty() && !isInterruptionRequested()) {
51 int id = queue_.front(); 55 int id = queue_.front();
52 56
53 /* unlock the mutex for a long blocking operation, so items 57 /* unlock the mutex for a long blocking operation, so items
61 queue_mutex_.unlock(); 65 queue_mutex_.unlock();
62 66
63 emit NeedRefresh(); 67 emit NeedRefresh();
64 } 68 }
65 69
66 AnimeListPageSortFilter::AnimeListPageSortFilter(QObject* parent) : QSortFilterProxyModel(parent) { 70 AnimeListPageSortFilter::AnimeListPageSortFilter(QObject *parent) : QSortFilterProxyModel(parent)
67 } 71 {
68 72 }
69 bool AnimeListPageSortFilter::lessThan(const QModelIndex& l, const QModelIndex& r) const { 73
74 bool AnimeListPageSortFilter::lessThan(const QModelIndex &l, const QModelIndex &r) const
75 {
70 QVariant left = sourceModel()->data(l, sortRole()); 76 QVariant left = sourceModel()->data(l, sortRole());
71 QVariant right = sourceModel()->data(r, sortRole()); 77 QVariant right = sourceModel()->data(r, sortRole());
72 78
73 switch (left.userType()) { 79 switch (left.userType()) {
74 case QMetaType::Int: 80 case QMetaType::Int:
81 } 87 }
82 } 88 }
83 89
84 /* -------------------------------------------------- */ 90 /* -------------------------------------------------- */
85 91
86 AnimeListPageModel::AnimeListPageModel(QObject* parent, Anime::ListStatus _status) : QAbstractListModel(parent) { 92 AnimeListPageModel::AnimeListPageModel(QObject *parent, Anime::ListStatus _status) : QAbstractListModel(parent)
93 {
87 status = _status; 94 status = _status;
88 return; 95 return;
89 } 96 }
90 97
91 int AnimeListPageModel::rowCount(const QModelIndex& parent) const { 98 int AnimeListPageModel::rowCount(const QModelIndex &parent) const
99 {
92 return list.size(); 100 return list.size();
93 (void)(parent); 101 (void)(parent);
94 } 102 }
95 103
96 int AnimeListPageModel::columnCount(const QModelIndex& parent) const { 104 int AnimeListPageModel::columnCount(const QModelIndex &parent) const
105 {
97 return NB_COLUMNS; 106 return NB_COLUMNS;
98 (void)(parent); 107 (void)(parent);
99 } 108 }
100 109
101 QVariant AnimeListPageModel::headerData(const int section, const Qt::Orientation orientation, const int role) const { 110 QVariant AnimeListPageModel::headerData(const int section, const Qt::Orientation orientation, const int role) const
111 {
102 if (role == Qt::DisplayRole) { 112 if (role == Qt::DisplayRole) {
103 switch (section) { 113 switch (section) {
104 case AL_TITLE: return tr("Anime title"); 114 case AL_TITLE: return tr("Anime title");
105 case AL_PROGRESS: return tr("Progress"); 115 case AL_PROGRESS: return tr("Progress");
106 case AL_EPISODES: return tr("Episodes"); 116 case AL_EPISODES: return tr("Episodes");
131 } 141 }
132 } 142 }
133 return QAbstractListModel::headerData(section, orientation, role); 143 return QAbstractListModel::headerData(section, orientation, role);
134 } 144 }
135 145
136 QVariant AnimeListPageModel::data(const QModelIndex& index, int role) const { 146 QVariant AnimeListPageModel::data(const QModelIndex &index, int role) const
147 {
137 if (!index.isValid()) 148 if (!index.isValid())
138 return QVariant(); 149 return QVariant();
139 switch (role) { 150 switch (role) {
140 case Qt::DisplayRole: 151 case Qt::DisplayRole:
141 switch (index.column()) { 152 switch (index.column()) {
142 case AL_TITLE: return Strings::ToQString(list[index.row()].GetUserPreferredTitle()); 153 case AL_TITLE: return Strings::ToQString(list[index.row()].GetUserPreferredTitle());
143 case AL_PROGRESS: 154 case AL_PROGRESS:
144 return QString::number(list[index.row()].GetUserProgress()) + "/" + 155 return QString::number(list[index.row()].GetUserProgress()) + "/" +
145 QString::number(list[index.row()].GetEpisodes()); 156 QString::number(list[index.row()].GetEpisodes());
146 case AL_EPISODES: return list[index.row()].GetEpisodes(); 157 case AL_EPISODES: return list[index.row()].GetEpisodes();
147 case AL_SCORE: return Strings::ToQString(list[index.row()].GetUserPresentableScore()); 158 case AL_SCORE: return Strings::ToQString(list[index.row()].GetUserPresentableScore());
148 case AL_TYPE: return Strings::ToQString(Translate::ToString(list[index.row()].GetFormat())); 159 case AL_TYPE: return Strings::ToQString(Translate::ToString(list[index.row()].GetFormat()));
149 case AL_SEASON: return Strings::ToQString(Translate::ToLocalString(list[index.row()].GetSeason())); 160 case AL_SEASON: return Strings::ToQString(Translate::ToLocalString(list[index.row()].GetSeason()));
150 case AL_AVG_SCORE: return QString::number(list[index.row()].GetAudienceScore()) + "%"; 161 case AL_AVG_SCORE: return QString::number(list[index.row()].GetAudienceScore()) + "%";
151 case AL_STARTED: return list[index.row()].GetUserDateStarted().GetAsQDate(); 162 case AL_STARTED: return list[index.row()].GetUserDateStarted().GetAsQDate();
152 case AL_COMPLETED: return list[index.row()].GetUserDateCompleted().GetAsQDate(); 163 case AL_COMPLETED: return list[index.row()].GetUserDateCompleted().GetAsQDate();
153 case AL_UPDATED: { 164 case AL_UPDATED: {
154 if (list[index.row()].GetUserTimeUpdated() == 0) 165 if (list[index.row()].GetUserTimeUpdated() == 0)
155 return QString("-"); 166 return QString("-");
156 return Strings::ToQString(Time::GetSecondsAsRelativeString(Time::GetSystemTime() - list[index.row()].GetUserTimeUpdated())); 167 return Strings::ToQString(Time::GetSecondsAsRelativeString(Time::GetSystemTime() -
168 list[index.row()].GetUserTimeUpdated()));
157 } 169 }
158 case AL_NOTES: return Strings::ToQString(list[index.row()].GetUserNotes()); 170 case AL_NOTES: return Strings::ToQString(list[index.row()].GetUserNotes());
159 default: return ""; 171 default: return "";
160 } 172 }
161 break; 173 break;
188 break; 200 break;
189 } 201 }
190 return QVariant(); 202 return QVariant();
191 } 203 }
192 204
193 Anime::Anime* AnimeListPageModel::GetAnimeFromIndex(QModelIndex index) { 205 Anime::Anime *AnimeListPageModel::GetAnimeFromIndex(QModelIndex index)
206 {
194 return &list.at(index.row()); 207 return &list.at(index.row());
195 } 208 }
196 209
197 void AnimeListPageModel::RefreshList() { 210 void AnimeListPageModel::RefreshList()
211 {
198 /* equivalent to hasChildren()... */ 212 /* equivalent to hasChildren()... */
199 if (!rowCount(index(0))) { 213 if (!rowCount(index(0))) {
200 beginInsertRows(QModelIndex(), 0, 0); 214 beginInsertRows(QModelIndex(), 0, 0);
201 endInsertRows(); 215 endInsertRows();
202 } 216 }
203 217
204 beginResetModel(); 218 beginResetModel();
205 219
206 list.clear(); 220 list.clear();
207 221
208 for (const auto& a : Anime::db.items) 222 for (const auto &a : Anime::db.items)
209 if (a.second.IsInUserList() && a.second.GetUserStatus() == status) 223 if (a.second.IsInUserList() && a.second.GetUserStatus() == status)
210 list.push_back(a.second); 224 list.push_back(a.second);
211 225
212 endResetModel(); 226 endResetModel();
213 } 227 }
214 228
215 /* ----------------------------------------------------------------- */ 229 /* ----------------------------------------------------------------- */
216 230
217 int AnimeListPage::VisibleColumnsCount() const { 231 int AnimeListPage::VisibleColumnsCount() const
232 {
218 int count = 0; 233 int count = 0;
219 234
220 for (int i = 0, end = tree_view->header()->count(); i < end; i++) { 235 for (int i = 0, end = tree_view->header()->count(); i < end; i++) {
221 if (!tree_view->isColumnHidden(i)) 236 if (!tree_view->isColumnHidden(i))
222 count++; 237 count++;
223 } 238 }
224 239
225 return count; 240 return count;
226 } 241 }
227 242
228 void AnimeListPage::SetColumnDefaults() { 243 void AnimeListPage::SetColumnDefaults()
244 {
229 tree_view->setColumnHidden(AnimeListPageModel::AL_SEASON, false); 245 tree_view->setColumnHidden(AnimeListPageModel::AL_SEASON, false);
230 tree_view->setColumnHidden(AnimeListPageModel::AL_TYPE, false); 246 tree_view->setColumnHidden(AnimeListPageModel::AL_TYPE, false);
231 tree_view->setColumnHidden(AnimeListPageModel::AL_UPDATED, false); 247 tree_view->setColumnHidden(AnimeListPageModel::AL_UPDATED, false);
232 tree_view->setColumnHidden(AnimeListPageModel::AL_PROGRESS, false); 248 tree_view->setColumnHidden(AnimeListPageModel::AL_PROGRESS, false);
233 tree_view->setColumnHidden(AnimeListPageModel::AL_SCORE, false); 249 tree_view->setColumnHidden(AnimeListPageModel::AL_SCORE, false);
238 tree_view->setColumnHidden(AnimeListPageModel::AL_COMPLETED, true); 254 tree_view->setColumnHidden(AnimeListPageModel::AL_COMPLETED, true);
239 tree_view->setColumnHidden(AnimeListPageModel::AL_UPDATED, true); 255 tree_view->setColumnHidden(AnimeListPageModel::AL_UPDATED, true);
240 tree_view->setColumnHidden(AnimeListPageModel::AL_NOTES, true); 256 tree_view->setColumnHidden(AnimeListPageModel::AL_NOTES, true);
241 } 257 }
242 258
243 void AnimeListPage::UpdateAnime(int id) { 259 void AnimeListPage::UpdateAnime(int id)
260 {
244 /* this ought to just add to the thread's buffer. */ 261 /* this ought to just add to the thread's buffer. */
245 if (update_entry_thread_.isRunning()) 262 if (update_entry_thread_.isRunning())
246 update_entry_thread_.requestInterruption(); 263 update_entry_thread_.requestInterruption();
247 264
248 update_entry_thread_.AddToQueue(id); 265 update_entry_thread_.AddToQueue(id);
249 update_entry_thread_.start(); 266 update_entry_thread_.start();
250 } 267 }
251 268
252 void AnimeListPage::RemoveAnime(int id) { 269 void AnimeListPage::RemoveAnime(int id)
253 Anime::Anime& anime = Anime::db.items[id]; 270 {
271 Anime::Anime &anime = Anime::db.items[id];
254 anime.RemoveFromUserList(); 272 anime.RemoveFromUserList();
255 Refresh(); 273 Refresh();
256 } 274 }
257 275
258 void AnimeListPage::DisplayColumnHeaderMenu() { 276 void AnimeListPage::DisplayColumnHeaderMenu()
259 QMenu* menu = new QMenu(this); 277 {
278 QMenu *menu = new QMenu(this);
260 menu->setAttribute(Qt::WA_DeleteOnClose); 279 menu->setAttribute(Qt::WA_DeleteOnClose);
261 menu->setTitle(tr("Column visibility")); 280 menu->setTitle(tr("Column visibility"));
262 menu->setToolTipsVisible(true); 281 menu->setToolTipsVisible(true);
263 282
264 for (int i = 0; i < AnimeListPageModel::NB_COLUMNS; i++) { 283 for (int i = 0; i < AnimeListPageModel::NB_COLUMNS; i++) {
265 if (i == AnimeListPageModel::AL_TITLE) 284 if (i == AnimeListPageModel::AL_TITLE)
266 continue; 285 continue;
267 const auto column_name = 286 const auto column_name =
268 sort_models[tab_bar->currentIndex()]->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(); 287 sort_models[tab_bar->currentIndex()]->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
269 288
270 QAction* action = menu->addAction(column_name, this, [this, i](const bool checked) { 289 QAction *action = menu->addAction(column_name, this, [this, i](const bool checked) {
271 if (!checked && (VisibleColumnsCount() <= 1)) 290 if (!checked && (VisibleColumnsCount() <= 1))
272 return; 291 return;
273 292
274 tree_view->setColumnHidden(i, !checked); 293 tree_view->setColumnHidden(i, !checked);
275 294
291 // SaveSettings(); 310 // SaveSettings();
292 }); 311 });
293 menu->popup(QCursor::pos()); 312 menu->popup(QCursor::pos());
294 } 313 }
295 314
296 void AnimeListPage::DisplayListMenu() { 315 void AnimeListPage::DisplayListMenu()
316 {
297 QMenu *const menu = new QMenu(this); 317 QMenu *const menu = new QMenu(this);
298 menu->setAttribute(Qt::WA_DeleteOnClose); 318 menu->setAttribute(Qt::WA_DeleteOnClose);
299 menu->setToolTipsVisible(true); 319 menu->setToolTipsVisible(true);
300 320
301 AnimeListPageModel* source_model = 321 AnimeListPageModel *source_model =
302 reinterpret_cast<AnimeListPageModel*>(sort_models[tab_bar->currentIndex()]->sourceModel()); 322 reinterpret_cast<AnimeListPageModel *>(sort_models[tab_bar->currentIndex()]->sourceModel());
303 const QItemSelection selection = 323 const QItemSelection selection =
304 sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection()); 324 sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection());
305 325
306 std::set<Anime::Anime*> animes; 326 std::set<Anime::Anime *> animes;
307 for (const auto& index : selection.indexes()) { 327 for (const auto &index : selection.indexes()) {
308 if (!index.isValid()) 328 if (!index.isValid())
309 continue; 329 continue;
310 330
311 Anime::Anime *const anime = source_model->GetAnimeFromIndex(index); 331 Anime::Anime *const anime = source_model->GetAnimeFromIndex(index);
312 if (!anime) 332 if (!anime)
370 // Play random episode <Ctrl+R> (why?) 390 // Play random episode <Ctrl+R> (why?)
371 391
372 Anime::Anime *anime = *animes.begin(); 392 Anime::Anime *anime = *animes.begin();
373 393
374 menu->addAction(tr("Information"), [this, anime] { 394 menu->addAction(tr("Information"), [this, anime] {
375 InformationDialog* dialog = new InformationDialog( 395 InformationDialog *dialog = new InformationDialog(
376 anime, [this](Anime::Anime* anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, this); 396 anime, [this](Anime::Anime *anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO,
397 this);
377 398
378 dialog->show(); 399 dialog->show();
379 dialog->raise(); 400 dialog->raise();
380 dialog->activateWindow(); 401 dialog->activateWindow();
381 connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater); 402 connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater);
382 }); 403 });
383 404
384 menu->addSeparator(); 405 menu->addSeparator();
385 406
386 menu->addAction(tr("Edit"), [this, anime] { 407 menu->addAction(tr("Edit"), [this, anime] {
387 InformationDialog* dialog = new InformationDialog( 408 InformationDialog *dialog = new InformationDialog(
388 anime, [this](Anime::Anime* anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MY_LIST, this); 409 anime, [this](Anime::Anime *anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MY_LIST,
410 this);
389 411
390 dialog->show(); 412 dialog->show();
391 dialog->raise(); 413 dialog->raise();
392 dialog->activateWindow(); 414 dialog->activateWindow();
393 connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater); 415 connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater);
394 }); 416 });
395 menu->addAction(tr("Delete from list..."), [this, anime] { 417 menu->addAction(
396 RemoveAnime(anime->GetId()); 418 tr("Delete from list..."), [this, anime] { RemoveAnime(anime->GetId()); },
397 }, QKeySequence(QKeySequence::Delete)); 419 QKeySequence(QKeySequence::Delete));
398 420
399 menu->addSeparator(); 421 menu->addSeparator();
400 422
401 menu->addAction(tr("Open folder"), [this, anime] { 423 menu->addAction(tr("Open folder"), [this, anime] {
402 std::optional<std::filesystem::path> path = Library::db.GetAnimeFolder(anime->GetId()); 424 std::optional<std::filesystem::path> path = Library::db.GetAnimeFolder(anime->GetId());
403 if (!path) // ... 425 if (!path) // ...
404 return; 426 return;
405 427
406 QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(path.value().u8string()))); 428 QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(path.value().u8string())));
407 }); 429 });
408 menu->addAction(tr("Scan available episodes"), [this, anime] { 430 menu->addAction(tr("Scan available episodes"), [this, anime] { Library::db.Refresh(anime->GetId()); });
409 Library::db.Refresh(anime->GetId());
410 });
411 431
412 menu->addSeparator(); 432 menu->addSeparator();
413 433
414 { 434 {
415 QMenu *submenu = menu->addMenu(tr("Play episode")); 435 QMenu *submenu = menu->addMenu(tr("Play episode"));
426 // I think this is right? 446 // I think this is right?
427 if (progress > 0) { 447 if (progress > 0) {
428 menu->addAction(tr("Play last episode (#%1)").arg(progress), [this, anime, progress] { 448 menu->addAction(tr("Play last episode (#%1)").arg(progress), [this, anime, progress] {
429 const int id = anime->GetId(); 449 const int id = anime->GetId();
430 450
431 if (Library::db.items.find(id) == Library::db.items.end() 451 if (Library::db.items.find(id) == Library::db.items.end() ||
432 || Library::db.items[id].find(progress) == Library::db.items[id].end()) 452 Library::db.items[id].find(progress) == Library::db.items[id].end())
433 return; 453 return;
434 454
435 QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress].u8string()))); 455 QDesktopServices::openUrl(
456 QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress].u8string())));
436 }); 457 });
437 } 458 }
438 459
439 if (progress < episodes) { 460 if (progress < episodes) {
440 menu->addAction(tr("Play next episode (#%1)").arg(progress + 1), [this, anime, progress] { 461 menu->addAction(
441 const int id = anime->GetId(); 462 tr("Play next episode (#%1)").arg(progress + 1),
442 463 [this, anime, progress] {
443 if (Library::db.items.find(id) == Library::db.items.end() 464 const int id = anime->GetId();
444 || Library::db.items[id].find(progress + 1) == Library::db.items[id].end()) 465
445 return; 466 if (Library::db.items.find(id) == Library::db.items.end() ||
446 467 Library::db.items[id].find(progress + 1) == Library::db.items[id].end())
447 QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress + 1].u8string()))); 468 return;
448 }, QKeySequence(Qt::CTRL | Qt::Key_N)); 469
470 QDesktopServices::openUrl(
471 QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][progress + 1].u8string())));
472 },
473 QKeySequence(Qt::CTRL | Qt::Key_N));
449 } 474 }
450 475
451 menu->addAction(tr("Play random episode"), [this, anime, episodes] { 476 menu->addAction(
452 const int id = anime->GetId(); 477 tr("Play random episode"),
453 478 [this, anime, episodes] {
454 std::uniform_int_distribution<int> distrib(1, episodes); 479 const int id = anime->GetId();
455 const int episode = distrib(session.gen); 480
456 481 std::uniform_int_distribution<int> distrib(1, episodes);
457 if (Library::db.items.find(id) == Library::db.items.end() 482 const int episode = distrib(session.gen);
458 || Library::db.items[id].find(episode) == Library::db.items[id].end()) 483
459 return; 484 if (Library::db.items.find(id) == Library::db.items.end() ||
460 485 Library::db.items[id].find(episode) == Library::db.items[id].end())
461 QDesktopServices::openUrl(QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][episode].u8string()))); 486 return;
462 }, QKeySequence(Qt::CTRL | Qt::Key_R)); 487
488 QDesktopServices::openUrl(
489 QUrl::fromLocalFile(Strings::ToQString(Library::db.items[id][episode].u8string())));
490 },
491 QKeySequence(Qt::CTRL | Qt::Key_R));
463 492
464 menu->popup(QCursor::pos()); 493 menu->popup(QCursor::pos());
465 } else { 494 } else {
466 // Where are we now? 495 // Where are we now?
467 } 496 }
468 } 497 }
469 498
470 void AnimeListPage::ItemDoubleClicked() { 499 void AnimeListPage::ItemDoubleClicked()
500 {
471 /* throw out any other garbage */ 501 /* throw out any other garbage */
472 const QItemSelection selection = 502 const QItemSelection selection =
473 sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection()); 503 sort_models[tab_bar->currentIndex()]->mapSelectionToSource(tree_view->selectionModel()->selection());
474 if (!selection.indexes().first().isValid()) { 504 if (!selection.indexes().first().isValid()) {
475 return; 505 return;
476 } 506 }
477 507
478 AnimeListPageModel* source_model = 508 AnimeListPageModel *source_model =
479 reinterpret_cast<AnimeListPageModel*>(sort_models[tab_bar->currentIndex()]->sourceModel()); 509 reinterpret_cast<AnimeListPageModel *>(sort_models[tab_bar->currentIndex()]->sourceModel());
480 510
481 const QModelIndex index = source_model->index(selection.indexes().first().row()); 511 const QModelIndex index = source_model->index(selection.indexes().first().row());
482 Anime::Anime& anime = Anime::db.items[source_model->GetAnimeFromIndex(index)->GetId()]; 512 Anime::Anime &anime = Anime::db.items[source_model->GetAnimeFromIndex(index)->GetId()];
483 513
484 InformationDialog* dialog = new InformationDialog( 514 InformationDialog *dialog = new InformationDialog(
485 &anime, [this](Anime::Anime* anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, this); 515 &anime, [this](Anime::Anime *anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, this);
486 516
487 dialog->show(); 517 dialog->show();
488 dialog->raise(); 518 dialog->raise();
489 dialog->activateWindow(); 519 dialog->activateWindow();
490 connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater); 520 connect(dialog, &InformationDialog::finished, dialog, &InformationDialog::deleteLater);
491 } 521 }
492 522
493 void AnimeListPage::RefreshList() { 523 void AnimeListPage::RefreshList()
524 {
494 for (unsigned int i = 0; i < sort_models.size(); i++) 525 for (unsigned int i = 0; i < sort_models.size(); i++)
495 reinterpret_cast<AnimeListPageModel*>(sort_models[i]->sourceModel())->RefreshList(); 526 reinterpret_cast<AnimeListPageModel *>(sort_models[i]->sourceModel())->RefreshList();
496 } 527 }
497 528
498 void AnimeListPage::RefreshTabs() { 529 void AnimeListPage::RefreshTabs()
530 {
499 for (unsigned int i = 0; i < sort_models.size(); i++) 531 for (unsigned int i = 0; i < sort_models.size(); i++)
500 tab_bar->setTabText(i, Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])) + " (" + 532 tab_bar->setTabText(i, Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])) + " (" +
501 QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")"); 533 QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")");
502 } 534 }
503 535
504 void AnimeListPage::Refresh() { 536 void AnimeListPage::Refresh()
537 {
505 RefreshList(); 538 RefreshList();
506 RefreshTabs(); 539 RefreshTabs();
507 } 540 }
508 541
509 /* -------- QTabWidget replication begin --------- */ 542 /* -------- QTabWidget replication begin --------- */
510 543
511 void AnimeListPage::InitBasicStyle(QStyleOptionTabWidgetFrame* option) const { 544 void AnimeListPage::InitBasicStyle(QStyleOptionTabWidgetFrame *option) const
545 {
512 if (!option) 546 if (!option)
513 return; 547 return;
514 548
515 option->initFrom(this); 549 option->initFrom(this);
516 option->lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, this); 550 option->lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, this);
517 option->shape = QTabBar::RoundedNorth; 551 option->shape = QTabBar::RoundedNorth;
518 option->tabBarRect = tab_bar->geometry(); 552 option->tabBarRect = tab_bar->geometry();
519 } 553 }
520 554
521 void AnimeListPage::InitStyle(QStyleOptionTabWidgetFrame* option) const { 555 void AnimeListPage::InitStyle(QStyleOptionTabWidgetFrame *option) const
556 {
522 if (!option) 557 if (!option)
523 return; 558 return;
524 559
525 InitBasicStyle(option); 560 InitBasicStyle(option);
526 561
537 option->selectedTabRect = selected_tab_rect; 572 option->selectedTabRect = selected_tab_rect;
538 573
539 option->lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, this); 574 option->lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, this);
540 } 575 }
541 576
542 void AnimeListPage::SetupLayout() { 577 void AnimeListPage::SetupLayout()
578 {
543 QStyleOptionTabWidgetFrame option; 579 QStyleOptionTabWidgetFrame option;
544 InitStyle(&option); 580 InitStyle(&option);
545 581
546 QRect tabRect = style()->subElementRect(QStyle::SE_TabWidgetTabBar, &option, this); 582 QRect tabRect = style()->subElementRect(QStyle::SE_TabWidgetTabBar, &option, this);
547 tabRect.setLeft(tabRect.left() + 1); 583 tabRect.setLeft(tabRect.left() + 1);
550 586
551 tab_bar->setGeometry(tabRect); 587 tab_bar->setGeometry(tabRect);
552 tree_view->parentWidget()->setGeometry(contentsRect); 588 tree_view->parentWidget()->setGeometry(contentsRect);
553 } 589 }
554 590
555 void AnimeListPage::paintEvent(QPaintEvent*) { 591 void AnimeListPage::paintEvent(QPaintEvent *)
592 {
556 QStylePainter p(this); 593 QStylePainter p(this);
557 594
558 QStyleOptionTabWidgetFrame opt; 595 QStyleOptionTabWidgetFrame opt;
559 InitStyle(&opt); 596 InitStyle(&opt);
560 opt.rect = panelRect; 597 opt.rect = panelRect;
561 p.drawPrimitive(QStyle::PE_FrameTabWidget, opt); 598 p.drawPrimitive(QStyle::PE_FrameTabWidget, opt);
562 } 599 }
563 600
564 void AnimeListPage::resizeEvent(QResizeEvent* e) { 601 void AnimeListPage::resizeEvent(QResizeEvent *e)
602 {
565 QWidget::resizeEvent(e); 603 QWidget::resizeEvent(e);
566 SetupLayout(); 604 SetupLayout();
567 } 605 }
568 606
569 void AnimeListPage::showEvent(QShowEvent*) { 607 void AnimeListPage::showEvent(QShowEvent *)
608 {
570 SetupLayout(); 609 SetupLayout();
571 Refresh(); 610 Refresh();
572 } 611 }
573 612
574 /* --------- QTabWidget replication end ---------- */ 613 /* --------- QTabWidget replication end ---------- */
575 614
576 AnimeListPage::AnimeListPage(QWidget* parent) : QWidget(parent) { 615 AnimeListPage::AnimeListPage(QWidget *parent) : QWidget(parent)
616 {
577 /* Tab bar */ 617 /* Tab bar */
578 tab_bar = new QTabBar(this); 618 tab_bar = new QTabBar(this);
579 tab_bar->setExpanding(false); 619 tab_bar->setExpanding(false);
580 tab_bar->setDrawBase(false); 620 tab_bar->setDrawBase(false);
581 621
582 /* Tree view... */ 622 /* Tree view... */
583 QWidget* tree_widget = new QWidget(this); 623 QWidget *tree_widget = new QWidget(this);
584 tree_view = new QTreeView(tree_widget); 624 tree_view = new QTreeView(tree_widget);
585 tree_view->setUniformRowHeights(true); 625 tree_view->setUniformRowHeights(true);
586 tree_view->setAllColumnsShowFocus(false); 626 tree_view->setAllColumnsShowFocus(false);
587 tree_view->setAlternatingRowColors(true); 627 tree_view->setAlternatingRowColors(true);
588 tree_view->setSortingEnabled(true); 628 tree_view->setSortingEnabled(true);
592 tree_view->setContextMenuPolicy(Qt::CustomContextMenu); 632 tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
593 tree_view->setFrameShape(QFrame::NoFrame); 633 tree_view->setFrameShape(QFrame::NoFrame);
594 634
595 for (unsigned int i = 0; i < sort_models.size(); i++) { 635 for (unsigned int i = 0; i < sort_models.size(); i++) {
596 tab_bar->addTab(Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])) + " (" + 636 tab_bar->addTab(Strings::ToQString(Translate::ToString(Anime::ListStatuses[i])) + " (" +
597 QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")"); 637 QString::number(Anime::db.GetListsAnimeAmount(Anime::ListStatuses[i])) + ")");
598 sort_models[i] = new AnimeListPageSortFilter(tree_view); 638 sort_models[i] = new AnimeListPageSortFilter(tree_view);
599 sort_models[i]->setSourceModel(new AnimeListPageModel(this, Anime::ListStatuses[i])); 639 sort_models[i]->setSourceModel(new AnimeListPageModel(this, Anime::ListStatuses[i]));
600 sort_models[i]->setSortRole(Qt::UserRole); 640 sort_models[i]->setSortRole(Qt::UserRole);
601 sort_models[i]->setSortCaseSensitivity(Qt::CaseInsensitive); 641 sort_models[i]->setSortCaseSensitivity(Qt::CaseInsensitive);
602 } 642 }
613 tree_view->setColumnWidth(AnimeListPageModel::AL_STARTED, 90); 653 tree_view->setColumnWidth(AnimeListPageModel::AL_STARTED, 90);
614 tree_view->setColumnWidth(AnimeListPageModel::AL_COMPLETED, 90); 654 tree_view->setColumnWidth(AnimeListPageModel::AL_COMPLETED, 90);
615 tree_view->setColumnWidth(AnimeListPageModel::AL_UPDATED, 100); 655 tree_view->setColumnWidth(AnimeListPageModel::AL_UPDATED, 100);
616 tree_view->setColumnWidth(AnimeListPageModel::AL_NOTES, 100); 656 tree_view->setColumnWidth(AnimeListPageModel::AL_NOTES, 100);
617 657
618 QHBoxLayout* layout = new QHBoxLayout(tree_widget); 658 QHBoxLayout *layout = new QHBoxLayout(tree_widget);
619 layout->addWidget(tree_view); 659 layout->addWidget(tree_view);
620 layout->setContentsMargins(0, 0, 0, 0); 660 layout->setContentsMargins(0, 0, 0, 0);
621 661
622 /* Double click stuff */ 662 /* Double click stuff */
623 connect(tree_view, &QAbstractItemView::doubleClicked, this, &AnimeListPage::ItemDoubleClicked); 663 connect(tree_view, &QAbstractItemView::doubleClicked, this, &AnimeListPage::ItemDoubleClicked);
624 connect(tree_view, &QWidget::customContextMenuRequested, this, &AnimeListPage::DisplayListMenu); 664 connect(tree_view, &QWidget::customContextMenuRequested, this, &AnimeListPage::DisplayListMenu);
625 665
626 /* Enter & return keys */ 666 /* Enter & return keys */
627 connect(new QShortcut(Qt::Key_Return, tree_view, nullptr, nullptr, Qt::WidgetShortcut), &QShortcut::activated, this, 667 connect(new QShortcut(Qt::Key_Return, tree_view, nullptr, nullptr, Qt::WidgetShortcut), &QShortcut::activated, this,
628 &AnimeListPage::ItemDoubleClicked); 668 &AnimeListPage::ItemDoubleClicked);
629 669
630 connect(new QShortcut(Qt::Key_Enter, tree_view, nullptr, nullptr, Qt::WidgetShortcut), &QShortcut::activated, this, 670 connect(new QShortcut(Qt::Key_Enter, tree_view, nullptr, nullptr, Qt::WidgetShortcut), &QShortcut::activated, this,
631 &AnimeListPage::ItemDoubleClicked); 671 &AnimeListPage::ItemDoubleClicked);
632 672
633 tree_view->header()->setStretchLastSection(false); 673 tree_view->header()->setStretchLastSection(false);
634 tree_view->header()->setContextMenuPolicy(Qt::CustomContextMenu); 674 tree_view->header()->setContextMenuPolicy(Qt::CustomContextMenu);
635 connect(tree_view->header(), &QWidget::customContextMenuRequested, this, &AnimeListPage::DisplayColumnHeaderMenu); 675 connect(tree_view->header(), &QWidget::customContextMenuRequested, this, &AnimeListPage::DisplayColumnHeaderMenu);
636 676