all repos — mgba @ 7a91b4a98aef707d996f559c8427974a74585695

mGBA Game Boy Advance Emulator

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

  1/* Copyright (c) 2013-2015 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 "CheatsModel.h"
  7
  8#include "LogController.h"
  9#include "VFileDevice.h"
 10
 11#include <QFont>
 12#include <QSet>
 13
 14extern "C" {
 15#include "gba/cheats.h"
 16}
 17
 18using namespace QGBA;
 19
 20CheatsModel::CheatsModel(GBACheatDevice* device, QObject* parent)
 21	: QAbstractItemModel(parent)
 22	, m_device(device)
 23{
 24}
 25
 26QVariant CheatsModel::data(const QModelIndex& index, int role) const {
 27	if (!index.isValid()) {
 28		return QVariant();
 29	}
 30
 31	if (index.parent().isValid()) {
 32		int row = index.row();
 33		GBACheatSet* cheats = static_cast<GBACheatSet*>(index.internalPointer());
 34		const char* line = *StringListGetPointer(&cheats->lines, row);
 35		switch (role) {
 36		case Qt::DisplayRole:
 37			return line;
 38		case Qt::FontRole:
 39			return QFont("Courier New", 13);
 40		default:
 41			return QVariant();
 42		}
 43	}
 44
 45	if (index.row() >= GBACheatSetsSize(&m_device->cheats)) {
 46		return QVariant();
 47	}
 48
 49	int row = index.row();
 50	const GBACheatSet* cheats = *GBACheatSetsGetPointer(&m_device->cheats, index.row());
 51	switch (role) {
 52	case Qt::DisplayRole:
 53	case Qt::EditRole:
 54		return cheats->name ? cheats->name : tr("(untitled)");
 55	case Qt::CheckStateRole:
 56		return cheats->enabled ? Qt::Checked : Qt::Unchecked;
 57	default:
 58		return QVariant();
 59	}
 60}
 61
 62bool CheatsModel::setData(const QModelIndex& index, const QVariant& value, int role) {
 63	if (!index.isValid() || index.parent().isValid() || index.row() > GBACheatSetsSize(&m_device->cheats)) {
 64		return false;
 65	}
 66
 67	int row = index.row();
 68	GBACheatSet* cheats = *GBACheatSetsGetPointer(&m_device->cheats, index.row());
 69	switch (role) {
 70	case Qt::DisplayRole:
 71	case Qt::EditRole:
 72		if (cheats->name) {
 73			free(cheats->name);
 74			cheats->name = nullptr;
 75		}
 76		cheats->name = strdup(value.toString().toUtf8().constData());
 77		emit dataChanged(index, index);
 78		return true;
 79	case Qt::CheckStateRole:
 80		cheats->enabled = value == Qt::Checked;
 81		emit dataChanged(index, index);
 82		return true;
 83	default:
 84		return false;
 85	}
 86}
 87
 88QModelIndex CheatsModel::index(int row, int column, const QModelIndex& parent) const {
 89	if (parent.isValid()) {
 90		return createIndex(row, column, *GBACheatSetsGetPointer(&m_device->cheats, parent.row()));
 91	} else {
 92		return createIndex(row, column, nullptr);
 93	}
 94}
 95
 96QModelIndex CheatsModel::parent(const QModelIndex& index) const {
 97	if (!index.isValid()) {
 98		return QModelIndex();
 99	}
100	const GBACheatSet* cheats = static_cast<const GBACheatSet*>(index.internalPointer());
101	if (!cheats) {
102		return QModelIndex();
103	}
104	for (size_t i = 0; i < GBACheatSetsSize(&m_device->cheats); ++i) {
105		if (cheats == *GBACheatSetsGetPointer(&m_device->cheats, i)) {
106			return createIndex(i, 0, nullptr);
107		}
108	}
109	return QModelIndex();
110}
111
112Qt::ItemFlags CheatsModel::flags(const QModelIndex& index) const {
113	if (!index.isValid()) {
114		return 0;
115	}
116
117	if (index.parent().isValid()) {
118		return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
119	}
120
121	return Qt::ItemIsUserCheckable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
122}
123
124int CheatsModel::columnCount(const QModelIndex& parent) const {
125	return 1;
126}
127
128int CheatsModel::rowCount(const QModelIndex& parent) const {
129	if (parent.isValid()) {
130		if (parent.internalPointer()) {
131			return 0;
132		}
133		const GBACheatSet* set = *GBACheatSetsGetPointer(&m_device->cheats, parent.row());
134		return StringListSize(&set->lines);
135	}
136	return GBACheatSetsSize(&m_device->cheats);
137}
138
139GBACheatSet* CheatsModel::itemAt(const QModelIndex& index) {
140	if (!index.isValid()) {
141		return nullptr;
142	}
143	if (index.parent().isValid()) {
144		return static_cast<GBACheatSet*>(index.internalPointer());
145	}
146	if (index.row() >= GBACheatSetsSize(&m_device->cheats)) {
147		return nullptr;
148	}
149	return *GBACheatSetsGetPointer(&m_device->cheats, index.row());
150}
151
152void CheatsModel::removeAt(const QModelIndex& index) {
153	if (!index.isValid() || index.parent().isValid() || index.row() >= GBACheatSetsSize(&m_device->cheats)) {
154		return;
155	}
156	int row = index.row();
157	GBACheatSet* set = *GBACheatSetsGetPointer(&m_device->cheats, index.row());
158	beginRemoveRows(QModelIndex(), row, row);
159	GBACheatRemoveSet(m_device, set);
160	GBACheatSetDeinit(set);
161	delete set;
162	endInsertRows();
163}
164
165QString CheatsModel::toString(const QModelIndexList& indices) const {
166	QMap<int, GBACheatSet*> setOrder;
167	QMap<GBACheatSet*, QSet<size_t>> setIndices;
168	for (const QModelIndex& index : indices) {
169		GBACheatSet* set = static_cast<GBACheatSet*>(index.internalPointer());
170		if (!set) {
171			set = *GBACheatSetsGetPointer(&m_device->cheats, index.row());
172			setOrder[index.row()] = set;
173			QSet<size_t> range;
174			for (size_t i = 0; i < StringListSize(&set->lines); ++i) {
175				range.insert(i);
176			}
177			setIndices[set] = range;
178		} else {
179			setOrder[index.parent().row()] = set;
180			setIndices[set].insert(index.row());
181		}
182	}
183
184	QStringList strings;
185	QList<int> order = setOrder.keys();
186	std::sort(order.begin(), order.end());
187	for (int i : order) {
188		GBACheatSet* set = setOrder[i];
189		QList<size_t> indexOrdex = setIndices[set].toList();
190		std::sort(indexOrdex.begin(), indexOrdex.end());
191		for (size_t j : indexOrdex) {
192			strings.append(*StringListGetPointer(&set->lines, j));
193		}
194	}
195
196	return strings.join('\n');
197}
198
199void CheatsModel::beginAppendRow(const QModelIndex& index) {
200	if (index.parent().isValid()) {
201		beginInsertRows(index.parent(), rowCount(index.parent()), rowCount(index.parent()));
202		return;
203	}
204	beginInsertRows(index, rowCount(index), rowCount(index));
205}
206
207void CheatsModel::endAppendRow() {
208	endInsertRows();
209}
210
211void CheatsModel::loadFile(const QString& path) {
212	VFile* vf = VFileDevice::open(path, O_RDONLY);
213	if (!vf) {
214		LOG(WARN) << tr("Failed to open cheats file: %1").arg(path);
215		return;
216	}
217	beginResetModel();
218	GBACheatParseFile(m_device, vf);
219	endResetModel();
220	vf->close(vf);
221}
222
223void CheatsModel::saveFile(const QString& path) {
224	VFile* vf = VFileDevice::open(path, O_TRUNC | O_CREAT | O_WRONLY);
225	if (!vf) {
226		return;
227	}
228	GBACheatSaveFile(m_device, vf);
229	vf->close(vf);
230}
231
232void CheatsModel::addSet(GBACheatSet* set) {
233	beginInsertRows(QModelIndex(), GBACheatSetsSize(&m_device->cheats), GBACheatSetsSize(&m_device->cheats));
234	size_t size = GBACheatSetsSize(&m_device->cheats);
235	if (size) {
236		GBACheatSetCopyProperties(set, *GBACheatSetsGetPointer(&m_device->cheats, size - 1));
237	}
238	GBACheatAddSet(m_device, set);
239	endInsertRows();
240}
241
242void CheatsModel::invalidated() {
243	beginResetModel();
244	endResetModel();
245}