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}