all repos — mgba @ 3c65ac986e8d2bbf64e1f55371c03f06e0342ce7

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 <QFont>
  9#include <QSet>
 10
 11extern "C" {
 12#include "gba/cheats.h"
 13#include "util/vfs.h"
 14}
 15
 16using namespace QGBA;
 17
 18CheatsModel::CheatsModel(GBACheatDevice* device, QObject* parent)
 19	: QAbstractItemModel(parent)
 20	, m_device(device)
 21{
 22}
 23
 24QVariant CheatsModel::data(const QModelIndex& index, int role) const {
 25	if (!index.isValid()) {
 26		return QVariant();
 27	}
 28
 29	if (index.parent().isValid()) {
 30		int row = index.row();
 31		GBACheatSet* cheats = static_cast<GBACheatSet*>(index.internalPointer());
 32		const char* line = *StringListGetPointer(&cheats->lines, row);
 33		switch (role) {
 34		case Qt::DisplayRole:
 35			return line;
 36		case Qt::FontRole:
 37			return QFont("Courier New", 13);
 38		default:
 39			return QVariant();
 40		}
 41	}
 42
 43	int row = index.row();
 44	const GBACheatSet* cheats = *GBACheatSetsGetPointer(&m_device->cheats, index.row());
 45	switch (role) {
 46	case Qt::DisplayRole:
 47	case Qt::EditRole:
 48		return cheats->name ? cheats->name : tr("(untitled)");
 49	case Qt::CheckStateRole:
 50		return cheats->enabled ? Qt::Checked : Qt::Unchecked;
 51	default:
 52		return QVariant();
 53	}
 54}
 55
 56bool CheatsModel::setData(const QModelIndex& index, const QVariant& value, int role) {
 57	if (!index.isValid() || index.parent().isValid()) {
 58		return false;
 59	}
 60
 61	int row = index.row();
 62	GBACheatSet* cheats = *GBACheatSetsGetPointer(&m_device->cheats, index.row());
 63	switch (role) {
 64	case Qt::DisplayRole:
 65	case Qt::EditRole:
 66		if (cheats->name) {
 67			free(cheats->name);
 68			cheats->name = nullptr;
 69		}
 70		cheats->name = strdup(value.toString().toLocal8Bit().constData());
 71		emit dataChanged(index, index);
 72		return true;
 73	case Qt::CheckStateRole:
 74		cheats->enabled = value == Qt::Checked;
 75		emit dataChanged(index, index);
 76		return true;
 77	default:
 78		return false;
 79	}
 80}
 81
 82QModelIndex CheatsModel::index(int row, int column, const QModelIndex& parent) const {
 83	if (parent.isValid()) {
 84		return createIndex(row, column, *GBACheatSetsGetPointer(&m_device->cheats, parent.row()));
 85	} else {
 86		return createIndex(row, column, nullptr);
 87	}
 88}
 89
 90QModelIndex CheatsModel::parent(const QModelIndex& index) const {
 91	if (!index.isValid()) {
 92		return QModelIndex();
 93	}
 94	const GBACheatSet* cheats = static_cast<const GBACheatSet*>(index.internalPointer());
 95	if (!cheats) {
 96		return QModelIndex();
 97	}
 98	for (size_t i = 0; i < GBACheatSetsSize(&m_device->cheats); ++i) {
 99		if (cheats == *GBACheatSetsGetPointer(&m_device->cheats, i)) {
100			return createIndex(i, 0, nullptr);
101		}
102	}
103	return QModelIndex();
104}
105
106Qt::ItemFlags CheatsModel::flags(const QModelIndex &index) const {
107	if (!index.isValid()) {
108		return 0;
109	}
110
111	if (index.parent().isValid()) {
112		return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
113	}
114
115	return Qt::ItemIsUserCheckable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
116}
117
118int CheatsModel::columnCount(const QModelIndex& parent) const {
119	return 1;
120}
121
122int CheatsModel::rowCount(const QModelIndex& parent) const {
123	if (parent.isValid()) {
124		if (parent.internalPointer()) {
125			return 0;
126		}
127		const GBACheatSet* set = *GBACheatSetsGetPointer(&m_device->cheats, parent.row());
128		return StringListSize(&set->lines);
129	}
130	return GBACheatSetsSize(&m_device->cheats);
131}
132
133GBACheatSet* CheatsModel::itemAt(const QModelIndex& index) {
134	if (!index.isValid()) {
135		return nullptr;
136	}
137	if (index.parent().isValid()) {
138		return static_cast<GBACheatSet*>(index.internalPointer());
139	}
140	return *GBACheatSetsGetPointer(&m_device->cheats, index.row());
141}
142
143void CheatsModel::removeAt(const QModelIndex& index) {
144	if (!index.isValid() || index.parent().isValid()) {
145		return;
146	}
147	int row = index.row();
148	GBACheatSet* set = *GBACheatSetsGetPointer(&m_device->cheats, index.row());
149	beginRemoveRows(QModelIndex(), row, row);
150	GBACheatRemoveSet(m_device, set);
151	GBACheatSetDeinit(set);
152	delete set;
153	endInsertRows();
154
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 = VFileOpen(path.toLocal8Bit().constData(), 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 = VFileOpen(path.toLocal8Bit().constData(), 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}