all repos — mgba @ 40e9dfcf5c3f512fa332a48d5a478329971b0aca

mGBA Game Boy Advance Emulator

src/platform/qt/LibraryModel.cpp (view raw)

  1/* Copyright (c) 2013-2016 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "LibraryModel.h"
  7
  8#include <QFontMetrics>
  9
 10#include <mgba-util/vfs.h>
 11
 12using namespace QGBA;
 13
 14Q_DECLARE_METATYPE(mLibraryEntry);
 15
 16QMap<QString, LibraryModel::LibraryHandle*> LibraryModel::s_handles;
 17QMap<QString, LibraryModel::LibraryColumn> LibraryModel::s_columns;
 18
 19LibraryModel::LibraryModel(const QString& path, QObject* parent)
 20	: QAbstractItemModel(parent)
 21{
 22	if (s_columns.empty()) {
 23		s_columns["name"] = {
 24			tr("Name"),
 25			[](const mLibraryEntry& e) -> QString {
 26				if (e.title) {
 27					return QString::fromUtf8(e.title);
 28				}
 29				return QString::fromUtf8(e.filename);
 30			}
 31		};
 32		s_columns["filename"] = {
 33			tr("Filename"),
 34			[](const mLibraryEntry& e) -> QString {
 35				return QString::fromUtf8(e.filename);
 36			}
 37		};
 38		s_columns["size"] = {
 39			tr("Size"),
 40			[](const mLibraryEntry& e) -> QString {
 41				double size = e.filesize;
 42				QString unit = "B";
 43				if (size >= 1024.0) {
 44					size /= 1024.0;
 45					unit = "kiB";
 46				}
 47				if (size >= 1024.0) {
 48					size /= 1024.0;
 49					unit = "MiB";
 50				}
 51				return QString("%0 %1").arg(size, 0, 'f', 1).arg(unit);
 52			},
 53			Qt::AlignRight
 54		};
 55		s_columns["platform"] = {
 56			tr("Platform"),
 57			[](const mLibraryEntry& e) -> QString {
 58				int platform = e.platform;
 59				switch (platform) {
 60#ifdef M_CORE_GBA
 61				case PLATFORM_GBA:
 62					return tr("GBA");
 63#endif
 64#ifdef M_CORE_GB
 65				case PLATFORM_GB:
 66					return tr("GB");
 67#endif
 68				default:
 69					return tr("?");
 70				}
 71			}
 72		};
 73		s_columns["location"] = {
 74			tr("Location"),
 75			[](const mLibraryEntry& e) -> QString {
 76				return QString::fromUtf8(e.base);
 77			}
 78		};
 79		s_columns["crc32"] = {
 80			tr("CRC32"),
 81			[](const mLibraryEntry& e) -> QString {
 82				return QString("%0").arg(e.crc32, 8, 16, QChar('0'));
 83			}
 84		};
 85	}
 86	if (!path.isNull()) {
 87		if (s_handles.contains(path)) {
 88			m_library = s_handles[path];
 89			m_library->ref();
 90		} else {
 91			m_library = new LibraryHandle(mLibraryLoad(path.toUtf8().constData()), path);
 92			if (m_library->library) {
 93				s_handles[path] = m_library;
 94			} else {
 95				delete m_library;
 96				m_library = new LibraryHandle(mLibraryCreateEmpty());
 97			}
 98		}
 99	} else {
100		m_library = new LibraryHandle(mLibraryCreateEmpty());
101	}
102	mLibraryListingInit(&m_listings, 0);
103	memset(&m_constraints, 0, sizeof(m_constraints));
104	m_constraints.platform = PLATFORM_NONE;
105	m_columns.append(s_columns["name"]);
106	m_columns.append(s_columns["location"]);
107	m_columns.append(s_columns["platform"]);
108	m_columns.append(s_columns["size"]);
109	m_columns.append(s_columns["crc32"]);
110
111	connect(m_library->loader, SIGNAL(directoryLoaded(const QString&)), this, SLOT(directoryLoaded(const QString&)));
112}
113
114LibraryModel::~LibraryModel() {
115	clearConstraints();
116	mLibraryListingDeinit(&m_listings);
117	if (!m_library->deref()) {
118		s_handles.remove(m_library->path);
119		delete m_library;
120	}
121}
122
123void LibraryModel::loadDirectory(const QString& path) {
124	m_queue.append(path);
125	QMetaObject::invokeMethod(m_library->loader, "loadDirectory", Q_ARG(const QString&, path));
126}
127
128bool LibraryModel::entryAt(int row, mLibraryEntry* out) const {
129	if (mLibraryListingSize(&m_listings) <= row) {
130		return false;
131	}
132	*out = *mLibraryListingGetConstPointer(&m_listings, row);
133	return true;
134}
135
136VFile* LibraryModel::openVFile(const QModelIndex& index) const {
137	mLibraryEntry entry;
138	if (!entryAt(index.row(), &entry)) {
139		return nullptr;
140	}
141	return mLibraryOpenVFile(m_library->library, &entry);
142}
143
144QString LibraryModel::filename(const QModelIndex& index) const {
145	mLibraryEntry entry;
146	if (!entryAt(index.row(), &entry)) {
147		return QString();
148	}
149	return QString::fromUtf8(entry.filename);
150}
151
152QString LibraryModel::location(const QModelIndex& index) const {
153	mLibraryEntry entry;
154	if (!entryAt(index.row(), &entry)) {
155		return QString();
156	}
157	return QString::fromUtf8(entry.base);
158}
159
160QVariant LibraryModel::data(const QModelIndex& index, int role) const {
161	if (!index.isValid()) {
162		return QVariant();
163	}
164	mLibraryEntry entry;
165	if (!entryAt(index.row(), &entry)) {
166		return QVariant();
167	}
168	if (role == Qt::UserRole) {
169		return QVariant::fromValue(entry);
170	}
171	if (index.column() >= m_columns.count()) {
172		return QVariant();
173	}
174	switch (role) {
175	case Qt::DisplayRole:
176		return m_columns[index.column()].value(entry);
177	case Qt::SizeHintRole: {
178		QFontMetrics fm((QFont()));
179		return fm.size(Qt::TextSingleLine, m_columns[index.column()].value(entry));
180	}
181	case Qt::TextAlignmentRole:
182		return m_columns[index.column()].alignment;
183	default:
184		return QVariant();
185	}
186}
187
188QVariant LibraryModel::headerData(int section, Qt::Orientation orientation, int role) const {
189	if (role != Qt::DisplayRole) {
190		return QAbstractItemModel::headerData(section, orientation, role);
191	}
192	if (orientation == Qt::Horizontal) {
193		if (section >= m_columns.count()) {
194			return QVariant();
195		}
196		return m_columns[section].name;
197	}
198	return section;
199}
200
201QModelIndex LibraryModel::index(int row, int column, const QModelIndex& parent) const {
202	if (parent.isValid()) {
203		return QModelIndex();
204	}
205	return createIndex(row, column, nullptr);
206}
207
208QModelIndex LibraryModel::parent(const QModelIndex&) const {
209	return QModelIndex();
210}
211
212int LibraryModel::columnCount(const QModelIndex& parent) const {
213	if (parent.isValid()) {
214		return 0;
215	}
216	return m_columns.count();
217}
218
219int LibraryModel::rowCount(const QModelIndex& parent) const {
220	if (parent.isValid()) {
221		return 0;
222	}
223	return mLibraryCount(m_library->library, &m_constraints);
224}
225
226void LibraryModel::attachGameDB(const NoIntroDB* gameDB) {
227	mLibraryAttachGameDB(m_library->library, gameDB);
228}
229
230void LibraryModel::constrainBase(const QString& path) {
231	clearConstraints();
232	if (m_constraints.base) {
233		free(const_cast<char*>(m_constraints.base));
234	}
235	m_constraints.base = strdup(path.toUtf8().constData());
236	reload();
237}
238
239void LibraryModel::clearConstraints() {
240	if (m_constraints.base) {
241		free(const_cast<char*>(m_constraints.base));
242	}
243	if (m_constraints.filename) {
244		free(const_cast<char*>(m_constraints.filename));
245	}
246	if (m_constraints.title) {
247		free(const_cast<char*>(m_constraints.title));
248	}
249	memset(&m_constraints, 0, sizeof(m_constraints));
250	size_t i;
251	for (i = 0; i < mLibraryListingSize(&m_listings); ++i) {
252		mLibraryEntryFree(mLibraryListingGetPointer(&m_listings, i));
253	}
254	mLibraryListingClear(&m_listings);
255}
256
257void LibraryModel::reload() {
258	mLibraryGetEntries(m_library->library, &m_listings, 0, 0, m_constraints.base ? &m_constraints : nullptr);
259}
260
261void LibraryModel::directoryLoaded(const QString& path) {
262	m_queue.removeOne(path);
263	beginResetModel();
264	endResetModel();
265	if (m_queue.empty()) {
266		emit doneLoading();
267	}
268}
269
270LibraryModel::LibraryColumn::LibraryColumn() {
271}
272
273LibraryModel::LibraryColumn::LibraryColumn(const QString& name, std::function<QString(const mLibraryEntry&)> value, int alignment)
274	: name(name)
275	, value(value)
276	, alignment(alignment)
277{
278}
279
280LibraryModel::LibraryHandle::LibraryHandle(mLibrary* lib, const QString& p)
281	: library(lib)
282	, loader(new LibraryLoader(library))
283	, path(p)
284	, m_ref(1)
285{
286	if (!library) {
287		return;
288	}
289	loader->moveToThread(&m_loaderThread);
290	m_loaderThread.setObjectName("Library Loader Thread");
291	m_loaderThread.start();
292}
293
294LibraryModel::LibraryHandle::~LibraryHandle() {
295	m_loaderThread.quit();
296	m_loaderThread.wait();
297	if (library) {
298		mLibraryDestroy(library);
299	}
300}
301
302void LibraryModel::LibraryHandle::ref() {
303	++m_ref;
304}
305
306bool LibraryModel::LibraryHandle::deref() {
307	--m_ref;
308	return m_ref > 0;
309}
310
311LibraryLoader::LibraryLoader(mLibrary* library, QObject* parent)
312	: QObject(parent)
313	, m_library(library)
314{
315}
316
317void LibraryLoader::loadDirectory(const QString& path) {
318	mLibraryLoadDirectory(m_library, path.toUtf8().constData());
319	emit directoryLoaded(path);
320}