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}