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 "GBAApp.h"
9#include "LogController.h"
10#include "VFileDevice.h"
11
12#include <QSet>
13
14#include <mgba/core/cheats.h>
15#include <mgba-util/vfs.h>
16
17using namespace QGBA;
18
19CheatsModel::CheatsModel(mCheatDevice* device, QObject* parent)
20 : QAbstractItemModel(parent)
21 , m_device(device)
22{
23 m_font = GBAApp::monospaceFont();
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 ((size_t) index.row() >= mCheatSetsSize(&m_device->cheats)) {
46 return QVariant();
47 }
48
49 const mCheatSet* cheats = *mCheatSetsGetPointer(&m_device->cheats, index.row());
50 switch (role) {
51 case Qt::DisplayRole:
52 case Qt::EditRole:
53 return cheats->name ? cheats->name : tr("(untitled)");
54 case Qt::CheckStateRole:
55 return cheats->enabled ? Qt::Checked : Qt::Unchecked;
56 default:
57 return QVariant();
58 }
59}
60
61bool CheatsModel::setData(const QModelIndex& index, const QVariant& value, int role) {
62 if (!index.isValid() || index.parent().isValid() || (size_t) index.row() > mCheatSetsSize(&m_device->cheats)) {
63 return false;
64 }
65
66 mCheatSet* cheats = *mCheatSetsGetPointer(&m_device->cheats, index.row());
67 switch (role) {
68 case Qt::DisplayRole:
69 case Qt::EditRole:
70 mCheatSetRename(cheats, value.toString().toUtf8().constData());
71 mCheatAutosave(m_device);
72 emit dataChanged(index, index);
73 return true;
74 case Qt::CheckStateRole:
75 cheats->enabled = value == Qt::Checked;
76 mCheatAutosave(m_device);
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, *mCheatSetsGetPointer(&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 mCheatSet* cheats = static_cast<const mCheatSet*>(index.internalPointer());
97 if (!cheats) {
98 return QModelIndex();
99 }
100 for (size_t i = 0; i < mCheatSetsSize(&m_device->cheats); ++i) {
101 if (cheats == *mCheatSetsGetPointer(&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&) 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 mCheatSet* set = *mCheatSetsGetPointer(&m_device->cheats, parent.row());
130 return StringListSize(&set->lines);
131 }
132 return mCheatSetsSize(&m_device->cheats);
133}
134
135mCheatSet* CheatsModel::itemAt(const QModelIndex& index) {
136 if (!index.isValid()) {
137 return nullptr;
138 }
139 if (index.parent().isValid()) {
140 return static_cast<mCheatSet*>(index.internalPointer());
141 }
142 if ((size_t) index.row() >= mCheatSetsSize(&m_device->cheats)) {
143 return nullptr;
144 }
145 return *mCheatSetsGetPointer(&m_device->cheats, index.row());
146}
147
148void CheatsModel::removeAt(const QModelIndex& index) {
149 if (!index.isValid() || index.parent().isValid() || (size_t) index.row() >= mCheatSetsSize(&m_device->cheats)) {
150 return;
151 }
152 int row = index.row();
153 mCheatSet* set = *mCheatSetsGetPointer(&m_device->cheats, index.row());
154 beginRemoveRows(QModelIndex(), row, row);
155 mCheatRemoveSet(m_device, set);
156 mCheatSetDeinit(set);
157 endRemoveRows();
158 mCheatAutosave(m_device);
159}
160
161QString CheatsModel::toString(const QModelIndexList& indices) const {
162 QMap<int, mCheatSet*> setOrder;
163 QMap<mCheatSet*, QSet<size_t>> setIndices;
164 for (const QModelIndex& index : indices) {
165 mCheatSet* set = static_cast<mCheatSet*>(index.internalPointer());
166 if (!set) {
167 set = *mCheatSetsGetPointer(&m_device->cheats, index.row());
168 setOrder[index.row()] = set;
169 QSet<size_t> range;
170 for (size_t i = 0; i < StringListSize(&set->lines); ++i) {
171 range.insert(i);
172 }
173 setIndices[set] = range;
174 } else {
175 setOrder[index.parent().row()] = set;
176 setIndices[set].insert(index.row());
177 }
178 }
179
180 QStringList strings;
181 QList<int> order = setOrder.keys();
182 std::sort(order.begin(), order.end());
183 for (int i : order) {
184 mCheatSet* set = setOrder[i];
185 QList<size_t> indexOrdex = setIndices[set].values();
186 std::sort(indexOrdex.begin(), indexOrdex.end());
187 for (size_t j : indexOrdex) {
188 strings.append(*StringListGetPointer(&set->lines, j));
189 }
190 }
191
192 return strings.join('\n');
193}
194
195void CheatsModel::beginAppendRow(const QModelIndex& index) {
196 if (index.parent().isValid()) {
197 beginInsertRows(index.parent(), rowCount(index.parent()), rowCount(index.parent()));
198 return;
199 }
200 beginInsertRows(index, rowCount(index), rowCount(index));
201}
202
203void CheatsModel::endAppendRow() {
204 endInsertRows();
205 mCheatAutosave(m_device);
206}
207
208void CheatsModel::loadFile(const QString& path) {
209 VFile* vf = VFileDevice::open(path, O_RDONLY);
210 if (!vf) {
211 LOG(QT, WARN) << tr("Failed to open cheats file: %1").arg(path);
212 return;
213 }
214 beginResetModel();
215 mCheatParseFile(m_device, vf);
216 endResetModel();
217 vf->close(vf);
218}
219
220void CheatsModel::saveFile(const QString& path) {
221 VFile* vf = VFileDevice::open(path, O_TRUNC | O_CREAT | O_WRONLY);
222 if (!vf) {
223 return;
224 }
225 mCheatSaveFile(m_device, vf);
226 vf->close(vf);
227}
228
229void CheatsModel::addSet(mCheatSet* set) {
230 beginInsertRows(QModelIndex(), mCheatSetsSize(&m_device->cheats), mCheatSetsSize(&m_device->cheats));
231 size_t size = mCheatSetsSize(&m_device->cheats);
232 if (size) {
233 set->copyProperties(set, *mCheatSetsGetPointer(&m_device->cheats, size - 1));
234 }
235 mCheatAddSet(m_device, set);
236 endInsertRows();
237 mCheatAutosave(m_device);
238}
239
240void CheatsModel::invalidated() {
241 beginResetModel();
242 endResetModel();
243}