all repos — mgba @ f836a67863e66499c812c8d963d37ff56ad10638

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