all repos — mgba @ e0a6af087e83d79b53ad5352511c7e02c557b93f

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