src/platform/qt/library/LibraryController.cpp (view raw)
1/* Copyright (c) 2014-2017 waddlesplash
2 * Copyright (c) 2014-2020 Jeffrey Pfau
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7#include "LibraryController.h"
8
9#include "ConfigController.h"
10#include "GBAApp.h"
11#include "LibraryGrid.h"
12#include "LibraryTree.h"
13
14using namespace QGBA;
15
16LibraryEntry::LibraryEntry(mLibraryEntry* entry)
17 : entry(entry)
18 , m_fullpath(QString("%1/%2").arg(entry->base, entry->filename))
19{
20}
21
22void AbstractGameList::addEntries(QList<LibraryEntryRef> items) {
23 for (LibraryEntryRef o : items) {
24 addEntry(o);
25 }
26}
27void AbstractGameList::removeEntries(QList<LibraryEntryRef> items) {
28 for (LibraryEntryRef o : items) {
29 removeEntry(o);
30 }
31}
32
33LibraryController::LibraryController(QWidget* parent, const QString& path, ConfigController* config)
34 : QStackedWidget(parent)
35 , m_config(config)
36{
37 mLibraryListingInit(&m_listing, 0);
38
39 if (!path.isNull()) {
40 // This can return NULL if the library is already open
41 m_library = std::shared_ptr<mLibrary>(mLibraryLoad(path.toUtf8().constData()), mLibraryDestroy);
42 }
43 if (!m_library) {
44 m_library = std::shared_ptr<mLibrary>(mLibraryCreateEmpty(), mLibraryDestroy);
45 }
46
47 mLibraryAttachGameDB(m_library.get(), GBAApp::app()->gameDB());
48
49 m_libraryTree = std::make_unique<LibraryTree>(this);
50 addWidget(m_libraryTree->widget());
51
52 m_libraryGrid = std::make_unique<LibraryGrid>(this);
53 addWidget(m_libraryGrid->widget());
54
55 m_currentStyle = LibraryStyle::STYLE_TREE; // Make sure setViewStyle does something
56 setViewStyle(LibraryStyle::STYLE_LIST);
57 refresh();
58}
59
60LibraryController::~LibraryController() {
61 freeLibrary();
62 mLibraryListingDeinit(&m_listing);
63}
64
65void LibraryController::setViewStyle(LibraryStyle newStyle) {
66 if (m_currentStyle == newStyle) {
67 return;
68 }
69 m_currentStyle = newStyle;
70
71 AbstractGameList* newCurrentList = nullptr;
72 if (newStyle == LibraryStyle::STYLE_LIST || newStyle == LibraryStyle::STYLE_TREE) {
73 newCurrentList = m_libraryTree.get();
74 } else {
75 newCurrentList = m_libraryGrid.get();
76 }
77 newCurrentList->selectEntry(selectedEntry());
78 newCurrentList->setViewStyle(newStyle);
79 setCurrentWidget(newCurrentList->widget());
80 m_currentList = newCurrentList;
81}
82
83void LibraryController::selectEntry(LibraryEntryRef entry) {
84 if (!m_currentList) {
85 return;
86 }
87 m_currentList->selectEntry(entry);
88}
89
90LibraryEntryRef LibraryController::selectedEntry() {
91 if (!m_currentList) {
92 return LibraryEntryRef();
93 }
94 return m_currentList->selectedEntry();
95}
96
97VFile* LibraryController::selectedVFile() {
98 LibraryEntryRef entry = selectedEntry();
99 if (entry) {
100 return mLibraryOpenVFile(m_library.get(), entry->entry);
101 } else {
102 return nullptr;
103 }
104}
105
106QPair<QString, QString> LibraryController::selectedPath() {
107 LibraryEntryRef e = selectedEntry();
108 return e ? qMakePair(e->base(), e->filename()) : qMakePair<QString, QString>("", "");
109}
110
111void LibraryController::addDirectory(const QString& dir, bool recursive) {
112 // The worker thread temporarily owns the library
113 std::shared_ptr<mLibrary> library = m_library;
114 m_libraryJob = GBAApp::app()->submitWorkerJob(std::bind(&LibraryController::loadDirectory, this, dir, recursive), this, [this, library]() {
115 m_libraryJob = -1;
116 refresh();
117 });
118}
119
120void LibraryController::clear() {
121 if (m_libraryJob > 0) {
122 return;
123 }
124
125 mLibraryClear(m_library.get());
126 refresh();
127}
128
129void LibraryController::refresh() {
130 if (m_libraryJob > 0) {
131 return;
132 }
133
134 setDisabled(true);
135
136 QStringList allEntries;
137 QList<LibraryEntryRef> newEntries;
138
139 freeLibrary();
140 mLibraryGetEntries(m_library.get(), &m_listing, 0, 0, nullptr);
141 for (size_t i = 0; i < mLibraryListingSize(&m_listing); i++) {
142 mLibraryEntry* entry = mLibraryListingGetPointer(&m_listing, i);
143 QString fullpath = QString("%1/%2").arg(entry->base, entry->filename);
144 if (m_entries.contains(fullpath)) {
145 m_entries.value(fullpath)->entry = entry;
146 } else {
147 LibraryEntryRef libentry = std::make_shared<LibraryEntry>(entry);
148 m_entries.insert(fullpath, libentry);
149 newEntries.append(libentry);
150 }
151 allEntries.append(fullpath);
152 }
153
154 // Check for entries that were removed
155 QList<LibraryEntryRef> removedEntries;
156 for (QString& path : m_entries.keys()) {
157 if (!allEntries.contains(path)) {
158 removedEntries.append(m_entries.value(path));
159 m_entries.remove(path);
160 }
161 }
162
163 m_libraryTree->addEntries(newEntries);
164 m_libraryGrid->addEntries(newEntries);
165
166 m_libraryTree->removeEntries(removedEntries);
167 m_libraryGrid->removeEntries(removedEntries);
168
169 setDisabled(false);
170 selectLastBootedGame();
171 emit doneLoading();
172}
173
174void LibraryController::selectLastBootedGame() {
175 if (!m_config || m_config->getMRU().isEmpty()) {
176 return;
177 }
178 const QString lastfile = m_config->getMRU().first();
179 if (m_entries.contains(lastfile)) {
180 selectEntry(m_entries.value(lastfile));
181 }
182}
183
184void LibraryController::loadDirectory(const QString& dir, bool recursive) {
185 // This class can get deleted during this function (sigh) so we need to hold onto this
186 std::shared_ptr<mLibrary> library = m_library;
187 mLibraryLoadDirectory(library.get(), dir.toUtf8().constData(), recursive);
188}
189
190void LibraryController::freeLibrary() {
191 for (size_t i = 0; i < mLibraryListingSize(&m_listing); ++i) {
192 mLibraryEntryFree(mLibraryListingGetPointer(&m_listing, i));
193 }
194 mLibraryListingClear(&m_listing);
195}