all repos — mgba @ e5161b766d6b8bdd21a174724b435c68185462e0

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 <QSet>
 12
 13#include <mgba/core/cheats.h>
 14#include <mgba-util/vfs.h>
 15
 16using namespace QGBA;
 17
 18CheatsModel::CheatsModel(mCheatDevice* device, QObject* parent)
 19	: QAbstractItemModel(parent)
 20	, m_device(device)
 21{
 22	m_font.setFamily("Source Code Pro");
 23	m_font.setStyleHint(QFont::Monospace);
 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		mCheatSet* cheats = static_cast<mCheatSet*>(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 m_font;
 40		default:
 41			return QVariant();
 42		}
 43	}
 44
 45	if (index.row() >= mCheatSetsSize(&m_device->cheats)) {
 46		return QVariant();
 47	}
 48
 49	int row = index.row();
 50	const mCheatSet* cheats = *mCheatSetsGetPointer(&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() > mCheatSetsSize(&m_device->cheats)) {
 64		return false;
 65	}
 66
 67	int row = index.row();
 68	mCheatSet* cheats = *mCheatSetsGetPointer(&m_device->cheats, index.row());
 69	switch (role) {
 70	case Qt::DisplayRole:
 71	case Qt::EditRole:
 72		mCheatSetRename(cheats, value.toString().toUtf8().constData());
 73		mCheatAutosave(m_device);
 74		emit dataChanged(index, index);
 75		return true;
 76	case Qt::CheckStateRole:
 77		cheats->enabled = value == Qt::Checked;
 78		mCheatAutosave(m_device);
 79		emit dataChanged(index, index);
 80		return true;
 81	default:
 82		return false;
 83	}
 84}
 85
 86QModelIndex CheatsModel::index(int row, int column, const QModelIndex& parent) const {
 87	if (parent.isValid()) {
 88		return createIndex(row, column, *mCheatSetsGetPointer(&m_device->cheats, parent.row()));
 89	} else {
 90		return createIndex(row, column, nullptr);
 91	}
 92}
 93
 94QModelIndex CheatsModel::parent(const QModelIndex& index) const {
 95	if (!index.isValid()) {
 96		return QModelIndex();
 97	}
 98	const mCheatSet* cheats = static_cast<const mCheatSet*>(index.internalPointer());
 99	if (!cheats) {
100		return QModelIndex();
101	}
102	for (size_t i = 0; i < mCheatSetsSize(&m_device->cheats); ++i) {
103		if (cheats == *mCheatSetsGetPointer(&m_device->cheats, i)) {
104			return createIndex(i, 0, nullptr);
105		}
106	}
107	return QModelIndex();
108}
109
110Qt::ItemFlags CheatsModel::flags(const QModelIndex& index) const {
111	if (!index.isValid()) {
112		return 0;
113	}
114
115	if (index.parent().isValid()) {
116		return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
117	}
118
119	return Qt::ItemIsUserCheckable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
120}
121
122int CheatsModel::columnCount(const QModelIndex& parent) const {
123	return 1;
124}
125
126int CheatsModel::rowCount(const QModelIndex& parent) const {
127	if (parent.isValid()) {
128		if (parent.internalPointer()) {
129			return 0;
130		}
131		const mCheatSet* set = *mCheatSetsGetPointer(&m_device->cheats, parent.row());
132		return StringListSize(&set->lines);
133	}
134	return mCheatSetsSize(&m_device->cheats);
135}
136
137mCheatSet* CheatsModel::itemAt(const QModelIndex& index) {
138	if (!index.isValid()) {
139		return nullptr;
140	}
141	if (index.parent().isValid()) {
142		return static_cast<mCheatSet*>(index.internalPointer());
143	}
144	if (index.row() >= mCheatSetsSize(&m_device->cheats)) {
145		return nullptr;
146	}
147	return *mCheatSetsGetPointer(&m_device->cheats, index.row());
148}
149
150void CheatsModel::removeAt(const QModelIndex& index) {
151	if (!index.isValid() || index.parent().isValid() || index.row() >= mCheatSetsSize(&m_device->cheats)) {
152		return;
153	}
154	int row = index.row();
155	mCheatSet* set = *mCheatSetsGetPointer(&m_device->cheats, index.row());
156	beginRemoveRows(QModelIndex(), row, row);
157	mCheatRemoveSet(m_device, set);
158	mCheatSetDeinit(set);
159	endRemoveRows();
160	mCheatAutosave(m_device);
161}
162
163QString CheatsModel::toString(const QModelIndexList& indices) const {
164	QMap<int, mCheatSet*> setOrder;
165	QMap<mCheatSet*, QSet<size_t>> setIndices;
166	for (const QModelIndex& index : indices) {
167		mCheatSet* set = static_cast<mCheatSet*>(index.internalPointer());
168		if (!set) {
169			set = *mCheatSetsGetPointer(&m_device->cheats, index.row());
170			setOrder[index.row()] = set;
171			QSet<size_t> range;
172			for (size_t i = 0; i < StringListSize(&set->lines); ++i) {
173				range.insert(i);
174			}
175			setIndices[set] = range;
176		} else {
177			setOrder[index.parent().row()] = set;
178			setIndices[set].insert(index.row());
179		}
180	}
181
182	QStringList strings;
183	QList<int> order = setOrder.keys();
184	std::sort(order.begin(), order.end());
185	for (int i : order) {
186		mCheatSet* set = setOrder[i];
187		QList<size_t> indexOrdex = setIndices[set].toList();
188		std::sort(indexOrdex.begin(), indexOrdex.end());
189		for (size_t j : indexOrdex) {
190			strings.append(*StringListGetPointer(&set->lines, j));
191		}
192	}
193
194	return strings.join('\n');
195}
196
197void CheatsModel::beginAppendRow(const QModelIndex& index) {
198	if (index.parent().isValid()) {
199		beginInsertRows(index.parent(), rowCount(index.parent()), rowCount(index.parent()));
200		return;
201	}
202	beginInsertRows(index, rowCount(index), rowCount(index));
203}
204
205void CheatsModel::endAppendRow() {
206	endInsertRows();
207	mCheatAutosave(m_device);
208}
209
210void CheatsModel::loadFile(const QString& path) {
211	VFile* vf = VFileDevice::open(path, O_RDONLY);
212	if (!vf) {
213		LOG(QT, WARN) << tr("Failed to open cheats file: %1").arg(path);
214		return;
215	}
216	beginResetModel();
217	mCheatParseFile(m_device, vf);
218	endResetModel();
219	vf->close(vf);
220}
221
222void CheatsModel::saveFile(const QString& path) {
223	VFile* vf = VFileDevice::open(path, O_TRUNC | O_CREAT | O_WRONLY);
224	if (!vf) {
225		return;
226	}
227	mCheatSaveFile(m_device, vf);
228	vf->close(vf);
229}
230
231void CheatsModel::addSet(mCheatSet* set) {
232	beginInsertRows(QModelIndex(), mCheatSetsSize(&m_device->cheats), mCheatSetsSize(&m_device->cheats));
233	size_t size = mCheatSetsSize(&m_device->cheats);
234	if (size) {
235		set->copyProperties(set, *mCheatSetsGetPointer(&m_device->cheats, size - 1));
236	}
237	mCheatAddSet(m_device, set);
238	endInsertRows();
239	mCheatAutosave(m_device);
240}
241
242void CheatsModel::invalidated() {
243	beginResetModel();
244	endResetModel();
245}