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 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, *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& 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 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 (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() || 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 endInsertRows();
158}
159
160QString CheatsModel::toString(const QModelIndexList& indices) const {
161 QMap<int, mCheatSet*> setOrder;
162 QMap<mCheatSet*, QSet<size_t>> setIndices;
163 for (const QModelIndex& index : indices) {
164 mCheatSet* set = static_cast<mCheatSet*>(index.internalPointer());
165 if (!set) {
166 set = *mCheatSetsGetPointer(&m_device->cheats, index.row());
167 setOrder[index.row()] = set;
168 QSet<size_t> range;
169 for (size_t i = 0; i < StringListSize(&set->lines); ++i) {
170 range.insert(i);
171 }
172 setIndices[set] = range;
173 } else {
174 setOrder[index.parent().row()] = set;
175 setIndices[set].insert(index.row());
176 }
177 }
178
179 QStringList strings;
180 QList<int> order = setOrder.keys();
181 std::sort(order.begin(), order.end());
182 for (int i : order) {
183 mCheatSet* set = setOrder[i];
184 QList<size_t> indexOrdex = setIndices[set].toList();
185 std::sort(indexOrdex.begin(), indexOrdex.end());
186 for (size_t j : indexOrdex) {
187 strings.append(*StringListGetPointer(&set->lines, j));
188 }
189 }
190
191 return strings.join('\n');
192}
193
194void CheatsModel::beginAppendRow(const QModelIndex& index) {
195 if (index.parent().isValid()) {
196 beginInsertRows(index.parent(), rowCount(index.parent()), rowCount(index.parent()));
197 return;
198 }
199 beginInsertRows(index, rowCount(index), rowCount(index));
200}
201
202void CheatsModel::endAppendRow() {
203 endInsertRows();
204}
205
206void CheatsModel::loadFile(const QString& path) {
207 VFile* vf = VFileDevice::open(path, O_RDONLY);
208 if (!vf) {
209 LOG(QT, WARN) << tr("Failed to open cheats file: %1").arg(path);
210 return;
211 }
212 beginResetModel();
213 mCheatParseFile(m_device, vf);
214 endResetModel();
215 vf->close(vf);
216}
217
218void CheatsModel::saveFile(const QString& path) {
219 VFile* vf = VFileDevice::open(path, O_TRUNC | O_CREAT | O_WRONLY);
220 if (!vf) {
221 return;
222 }
223 mCheatSaveFile(m_device, vf);
224 vf->close(vf);
225}
226
227void CheatsModel::addSet(mCheatSet* set) {
228 beginInsertRows(QModelIndex(), mCheatSetsSize(&m_device->cheats), mCheatSetsSize(&m_device->cheats));
229 size_t size = mCheatSetsSize(&m_device->cheats);
230 if (size) {
231 set->copyProperties(set, *mCheatSetsGetPointer(&m_device->cheats, size - 1));
232 }
233 mCheatAddSet(m_device, set);
234 endInsertRows();
235}
236
237void CheatsModel::invalidated() {
238 beginResetModel();
239 endResetModel();
240}