all repos — mgba @ 480415c51e8a3f1af0535042a2dbaf35058e82c8

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 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	}
 64	if (!path.isNull()) {
 65		if (s_handles.contains(path)) {
 66			m_library = s_handles[path];
 67			m_library->ref();
 68		} else {
 69			m_library = new LibraryHandle(mLibraryLoad(path.toUtf8().constData()), path);
 70			s_handles[path] = m_library;
 71		}
 72	} else {
 73		m_library = new LibraryHandle(mLibraryCreateEmpty());
 74	}
 75	memset(&m_constraints, 0, sizeof(m_constraints));
 76	m_constraints.platform = PLATFORM_NONE;
 77	m_columns.append(s_columns["filename"]);
 78	m_columns.append(s_columns["platform"]);
 79	m_columns.append(s_columns["size"]);
 80
 81	connect(m_library->loader, SIGNAL(directoryLoaded(const QString&)), this, SLOT(directoryLoaded(const QString&)));
 82}
 83
 84LibraryModel::~LibraryModel() {
 85	clearConstraints();
 86	if (!m_library->deref()) {
 87		s_handles.remove(m_library->path);
 88		delete m_library;
 89	}
 90}
 91
 92void LibraryModel::loadDirectory(const QString& path) {
 93	m_queue.append(path);
 94	QMetaObject::invokeMethod(m_library->loader, "loadDirectory", Q_ARG(const QString&, path));
 95}
 96
 97bool LibraryModel::entryAt(int row, mLibraryEntry* out) const {
 98	mLibraryListing entries;
 99	mLibraryListingInit(&entries, 0);
100	if (!mLibraryGetEntries(m_library->library, &entries, 1, row, &m_constraints)) {
101		mLibraryListingDeinit(&entries);
102		return false;
103	}
104	*out = *mLibraryListingGetPointer(&entries, 0);
105	mLibraryListingDeinit(&entries);
106	return true;
107}
108
109VFile* LibraryModel::openVFile(const QModelIndex& index) const {
110	mLibraryEntry entry;
111	if (!entryAt(index.row(), &entry)) {
112		return nullptr;
113	}
114	return mLibraryOpenVFile(m_library->library, &entry);
115}
116
117QVariant LibraryModel::data(const QModelIndex& index, int role) const {
118	if (!index.isValid()) {
119		return QVariant();
120	}
121	mLibraryEntry entry;
122	if (!entryAt(index.row(), &entry)) {
123		return QVariant();
124	}
125	if (role == Qt::UserRole) {
126		return QVariant::fromValue(entry);
127	}
128	if (index.column() >= m_columns.count()) {
129		return QVariant();
130	}
131	switch (role) {
132	case Qt::DisplayRole:
133		return m_columns[index.column()].value(entry);
134	case Qt::SizeHintRole: {
135		QFontMetrics fm((QFont()));
136		return fm.size(Qt::TextSingleLine, m_columns[index.column()].value(entry));
137	}
138	default:
139		return QVariant();
140	}
141}
142
143QVariant LibraryModel::headerData(int section, Qt::Orientation orientation, int role) const {
144	if (role != Qt::DisplayRole) {
145		return QAbstractItemModel::headerData(section, orientation, role);
146	}
147	if (orientation == Qt::Horizontal) {
148		if (section >= m_columns.count()) {
149			return QVariant();
150		}
151		return m_columns[section].name;
152	}
153	return section;
154}
155
156QModelIndex LibraryModel::index(int row, int column, const QModelIndex& parent) const {
157	if (parent.isValid()) {
158		return QModelIndex();
159	}
160	return createIndex(row, column, nullptr);
161}
162
163QModelIndex LibraryModel::parent(const QModelIndex&) const {
164	return QModelIndex();
165}
166
167int LibraryModel::columnCount(const QModelIndex& parent) const {
168	if (parent.isValid()) {
169		return 0;
170	}
171	return m_columns.count();
172}
173
174int LibraryModel::rowCount(const QModelIndex& parent) const {
175	if (parent.isValid()) {
176		return 0;
177	}
178	return mLibraryCount(m_library->library, &m_constraints);
179}
180
181void LibraryModel::constrainBase(const QString& path) {
182	if (m_constraints.base) {
183		free(const_cast<char*>(m_constraints.base));
184	}
185	m_constraints.base = strdup(path.toUtf8().constData());
186}
187
188void LibraryModel::clearConstraints() {
189	if (m_constraints.base) {
190		free(const_cast<char*>(m_constraints.base));
191	}
192	if (m_constraints.filename) {
193		free(const_cast<char*>(m_constraints.filename));
194	}
195	if (m_constraints.title) {
196		free(const_cast<char*>(m_constraints.title));
197	}
198	memset(&m_constraints, 0, sizeof(m_constraints));
199}
200
201void LibraryModel::directoryLoaded(const QString& path) {
202	m_queue.removeOne(path);
203	beginResetModel();
204	endResetModel();
205	if (m_queue.empty()) {
206		emit doneLoading();
207	}
208}
209
210
211LibraryModel::LibraryHandle::LibraryHandle(mLibrary* lib, const QString& p)
212	: library(lib)
213	, loader(new LibraryLoader(library))
214	, path(p)
215	, m_ref(1)
216{
217	if (!library) {
218		return;
219	}
220	loader->moveToThread(&m_loaderThread);
221	m_loaderThread.setObjectName("Library Loader Thread");
222	m_loaderThread.start();
223}
224
225LibraryModel::LibraryHandle::~LibraryHandle() {
226	m_loaderThread.quit();
227	m_loaderThread.wait();
228	mLibraryDestroy(library);
229}
230
231void LibraryModel::LibraryHandle::ref() {
232	++m_ref;
233}
234
235bool LibraryModel::LibraryHandle::deref() {
236	--m_ref;
237	return m_ref > 0;
238}
239
240LibraryLoader::LibraryLoader(mLibrary* library, QObject* parent)
241	: QObject(parent)
242	, m_library(library)
243{
244}
245
246void LibraryLoader::loadDirectory(const QString& path) {
247	mLibraryLoadDirectory(m_library, path.toUtf8().constData());
248	emit directoryLoaded(path);
249}