all repos — mgba @ 91fd2c6b4768fbeb58c74d2f119e7d55685a9800

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["filename"] = {
 24			tr("Filename"),
 25			[](const mLibraryEntry& e) -> QString {
 26				return QString::fromUtf8(e.filename);
 27			}
 28		};
 29		s_columns["size"] = {
 30			tr("Size"),
 31			[](const mLibraryEntry& e) -> QString {
 32				double size = e.filesize;
 33				QString unit = "B";
 34				if (size > 1024.0) {
 35					size /= 1024.0;
 36					unit = "kiB";
 37				}
 38				if (size > 1024.0) {
 39					size /= 1024.0;
 40					unit = "MiB";
 41				}
 42				return QString("%0 %1").arg(size, 0, 'f', 1).arg(unit);
 43			}
 44		};
 45		s_columns["platform"] = {
 46			tr("Platform"),
 47			[](const mLibraryEntry& e) -> QString {
 48				int platform = e.platform;
 49				switch (platform) {
 50#ifdef M_CORE_GBA
 51				case PLATFORM_GBA:
 52					return tr("GBA");
 53#endif
 54#ifdef M_CORE_GB
 55				case PLATFORM_GB:
 56					return tr("GB");
 57#endif
 58				default:
 59					return tr("?");
 60				}
 61			}
 62		};
 63		s_columns["location"] = {
 64			tr("Location"),
 65			[](const mLibraryEntry& e) -> QString {
 66				return QString::fromUtf8(e.base);
 67			}
 68		};
 69	}
 70	if (!path.isNull()) {
 71		if (s_handles.contains(path)) {
 72			m_library = s_handles[path];
 73			m_library->ref();
 74		} else {
 75			m_library = new LibraryHandle(mLibraryLoad(path.toUtf8().constData()), path);
 76			s_handles[path] = m_library;
 77		}
 78	} else {
 79		m_library = new LibraryHandle(mLibraryCreateEmpty());
 80	}
 81	memset(&m_constraints, 0, sizeof(m_constraints));
 82	m_constraints.platform = PLATFORM_NONE;
 83	m_columns.append(s_columns["filename"]);
 84	m_columns.append(s_columns["location"]);
 85	m_columns.append(s_columns["platform"]);
 86	m_columns.append(s_columns["size"]);
 87
 88	connect(m_library->loader, SIGNAL(directoryLoaded(const QString&)), this, SLOT(directoryLoaded(const QString&)));
 89}
 90
 91LibraryModel::~LibraryModel() {
 92	clearConstraints();
 93	if (!m_library->deref()) {
 94		s_handles.remove(m_library->path);
 95		delete m_library;
 96	}
 97}
 98
 99void LibraryModel::loadDirectory(const QString& path) {
100	m_queue.append(path);
101	QMetaObject::invokeMethod(m_library->loader, "loadDirectory", Q_ARG(const QString&, path));
102}
103
104bool LibraryModel::entryAt(int row, mLibraryEntry* out) const {
105	mLibraryListing entries;
106	mLibraryListingInit(&entries, 0);
107	if (!mLibraryGetEntries(m_library->library, &entries, 1, row, &m_constraints)) {
108		mLibraryListingDeinit(&entries);
109		return false;
110	}
111	*out = *mLibraryListingGetPointer(&entries, 0);
112	mLibraryListingDeinit(&entries);
113	return true;
114}
115
116VFile* LibraryModel::openVFile(const QModelIndex& index) const {
117	mLibraryEntry entry;
118	if (!entryAt(index.row(), &entry)) {
119		return nullptr;
120	}
121	return mLibraryOpenVFile(m_library->library, &entry);
122}
123
124QString LibraryModel::filename(const QModelIndex& index) const {
125	mLibraryEntry entry;
126	if (!entryAt(index.row(), &entry)) {
127		return QString();
128	}
129	return QString::fromUtf8(entry.filename);
130}
131
132QString LibraryModel::location(const QModelIndex& index) const {
133	mLibraryEntry entry;
134	if (!entryAt(index.row(), &entry)) {
135		return QString();
136	}
137	return QString::fromUtf8(entry.base);
138}
139
140QVariant LibraryModel::data(const QModelIndex& index, int role) const {
141	if (!index.isValid()) {
142		return QVariant();
143	}
144	mLibraryEntry entry;
145	if (!entryAt(index.row(), &entry)) {
146		return QVariant();
147	}
148	if (role == Qt::UserRole) {
149		return QVariant::fromValue(entry);
150	}
151	if (index.column() >= m_columns.count()) {
152		return QVariant();
153	}
154	switch (role) {
155	case Qt::DisplayRole:
156		return m_columns[index.column()].value(entry);
157	case Qt::SizeHintRole: {
158		QFontMetrics fm((QFont()));
159		return fm.size(Qt::TextSingleLine, m_columns[index.column()].value(entry));
160	}
161	default:
162		return QVariant();
163	}
164}
165
166QVariant LibraryModel::headerData(int section, Qt::Orientation orientation, int role) const {
167	if (role != Qt::DisplayRole) {
168		return QAbstractItemModel::headerData(section, orientation, role);
169	}
170	if (orientation == Qt::Horizontal) {
171		if (section >= m_columns.count()) {
172			return QVariant();
173		}
174		return m_columns[section].name;
175	}
176	return section;
177}
178
179QModelIndex LibraryModel::index(int row, int column, const QModelIndex& parent) const {
180	if (parent.isValid()) {
181		return QModelIndex();
182	}
183	return createIndex(row, column, nullptr);
184}
185
186QModelIndex LibraryModel::parent(const QModelIndex&) const {
187	return QModelIndex();
188}
189
190int LibraryModel::columnCount(const QModelIndex& parent) const {
191	if (parent.isValid()) {
192		return 0;
193	}
194	return m_columns.count();
195}
196
197int LibraryModel::rowCount(const QModelIndex& parent) const {
198	if (parent.isValid()) {
199		return 0;
200	}
201	return mLibraryCount(m_library->library, &m_constraints);
202}
203
204void LibraryModel::constrainBase(const QString& path) {
205	if (m_constraints.base) {
206		free(const_cast<char*>(m_constraints.base));
207	}
208	m_constraints.base = strdup(path.toUtf8().constData());
209}
210
211void LibraryModel::clearConstraints() {
212	if (m_constraints.base) {
213		free(const_cast<char*>(m_constraints.base));
214	}
215	if (m_constraints.filename) {
216		free(const_cast<char*>(m_constraints.filename));
217	}
218	if (m_constraints.title) {
219		free(const_cast<char*>(m_constraints.title));
220	}
221	memset(&m_constraints, 0, sizeof(m_constraints));
222}
223
224void LibraryModel::directoryLoaded(const QString& path) {
225	m_queue.removeOne(path);
226	beginResetModel();
227	endResetModel();
228	if (m_queue.empty()) {
229		emit doneLoading();
230	}
231}
232
233
234LibraryModel::LibraryHandle::LibraryHandle(mLibrary* lib, const QString& p)
235	: library(lib)
236	, loader(new LibraryLoader(library))
237	, path(p)
238	, m_ref(1)
239{
240	if (!library) {
241		return;
242	}
243	loader->moveToThread(&m_loaderThread);
244	m_loaderThread.setObjectName("Library Loader Thread");
245	m_loaderThread.start();
246}
247
248LibraryModel::LibraryHandle::~LibraryHandle() {
249	m_loaderThread.quit();
250	m_loaderThread.wait();
251	mLibraryDestroy(library);
252}
253
254void LibraryModel::LibraryHandle::ref() {
255	++m_ref;
256}
257
258bool LibraryModel::LibraryHandle::deref() {
259	--m_ref;
260	return m_ref > 0;
261}
262
263LibraryLoader::LibraryLoader(mLibrary* library, QObject* parent)
264	: QObject(parent)
265	, m_library(library)
266{
267}
268
269void LibraryLoader::loadDirectory(const QString& path) {
270	mLibraryLoadDirectory(m_library, path.toUtf8().constData());
271	emit directoryLoaded(path);
272}