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
13extern "C" {
14#include "core/cheats.h"
15}
16
17using namespace QGBA;
18
19CheatsModel::CheatsModel(mCheatDevice* device, QObject* parent)
20 : QAbstractItemModel(parent)
21 , m_device(device)
22{
23 m_font.setFamily("Source Code Pro");
24 m_font.setStyleHint(QFont::Monospace);
25}
26
27QVariant CheatsModel::data(const QModelIndex& index, int role) const {
28 if (!index.isValid()) {
29 return QVariant();
30 }
31
32 if (index.parent().isValid()) {
33 int row = index.row();
34 mCheatSet* cheats = static_cast<mCheatSet*>(index.internalPointer());
35 const char* line = *StringListGetPointer(&cheats->lines, row);
36 switch (role) {
37 case Qt::DisplayRole:
38 return line;
39 case Qt::FontRole:
40 return m_font;
41 default:
42 return QVariant();
43 }
44 }
45
46 if (index.row() >= mCheatSetsSize(&m_device->cheats)) {
47 return QVariant();
48 }
49
50 int row = index.row();
51 const mCheatSet* cheats = *mCheatSetsGetPointer(&m_device->cheats, index.row());
52 switch (role) {
53 case Qt::DisplayRole:
54 case Qt::EditRole:
55 return cheats->name ? cheats->name : tr("(untitled)");
56 case Qt::CheckStateRole:
57 return cheats->enabled ? Qt::Checked : Qt::Unchecked;
58 default:
59 return QVariant();
60 }
61}
62
63bool CheatsModel::setData(const QModelIndex& index, const QVariant& value, int role) {
64 if (!index.isValid() || index.parent().isValid() || index.row() > mCheatSetsSize(&m_device->cheats)) {
65 return false;
66 }
67
68 int row = index.row();
69 mCheatSet* cheats = *mCheatSetsGetPointer(&m_device->cheats, index.row());
70 switch (role) {
71 case Qt::DisplayRole:
72 case Qt::EditRole:
73 mCheatSetRename(cheats, value.toString().toUtf8().constData());
74 emit dataChanged(index, index);
75 return true;
76 case Qt::CheckStateRole:
77 cheats->enabled = value == Qt::Checked;
78 emit dataChanged(index, index);
79 return true;
80 default:
81 return false;
82 }
83}
84
85QModelIndex CheatsModel::index(int row, int column, const QModelIndex& parent) const {
86 if (parent.isValid()) {
87 return createIndex(row, column, *mCheatSetsGetPointer(&m_device->cheats, parent.row()));
88 } else {
89 return createIndex(row, column, nullptr);
90 }
91}
92
93QModelIndex CheatsModel::parent(const QModelIndex& index) const {
94 if (!index.isValid()) {
95 return QModelIndex();
96 }
97 const mCheatSet* cheats = static_cast<const mCheatSet*>(index.internalPointer());
98 if (!cheats) {
99 return QModelIndex();
100 }
101 for (size_t i = 0; i < mCheatSetsSize(&m_device->cheats); ++i) {
102 if (cheats == *mCheatSetsGetPointer(&m_device->cheats, i)) {
103 return createIndex(i, 0, nullptr);
104 }
105 }
106 return QModelIndex();
107}
108
109Qt::ItemFlags CheatsModel::flags(const QModelIndex& index) const {
110 if (!index.isValid()) {
111 return 0;
112 }
113
114 if (index.parent().isValid()) {
115 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
116 }
117
118 return Qt::ItemIsUserCheckable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
119}
120
121int CheatsModel::columnCount(const QModelIndex& parent) const {
122 return 1;
123}
124
125int CheatsModel::rowCount(const QModelIndex& parent) const {
126 if (parent.isValid()) {
127 if (parent.internalPointer()) {
128 return 0;
129 }
130 const mCheatSet* set = *mCheatSetsGetPointer(&m_device->cheats, parent.row());
131 return StringListSize(&set->lines);
132 }
133 return mCheatSetsSize(&m_device->cheats);
134}
135
136mCheatSet* CheatsModel::itemAt(const QModelIndex& index) {
137 if (!index.isValid()) {
138 return nullptr;
139 }
140 if (index.parent().isValid()) {
141 return static_cast<mCheatSet*>(index.internalPointer());
142 }
143 if (index.row() >= mCheatSetsSize(&m_device->cheats)) {
144 return nullptr;
145 }
146 return *mCheatSetsGetPointer(&m_device->cheats, index.row());
147}
148
149void CheatsModel::removeAt(const QModelIndex& index) {
150 if (!index.isValid() || index.parent().isValid() || index.row() >= mCheatSetsSize(&m_device->cheats)) {
151 return;
152 }
153 int row = index.row();
154 mCheatSet* set = *mCheatSetsGetPointer(&m_device->cheats, index.row());
155 beginRemoveRows(QModelIndex(), row, row);
156 mCheatRemoveSet(m_device, set);
157 mCheatSetDeinit(set);
158 endInsertRows();
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].toList();
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}
206
207void CheatsModel::loadFile(const QString& path) {
208 VFile* vf = VFileDevice::open(path, O_RDONLY);
209 if (!vf) {
210 LOG(QT, WARN) << tr("Failed to open cheats file: %1").arg(path);
211 return;
212 }
213 beginResetModel();
214 mCheatParseFile(m_device, vf);
215 endResetModel();
216 vf->close(vf);
217}
218
219void CheatsModel::saveFile(const QString& path) {
220 VFile* vf = VFileDevice::open(path, O_TRUNC | O_CREAT | O_WRONLY);
221 if (!vf) {
222 return;
223 }
224 mCheatSaveFile(m_device, vf);
225 vf->close(vf);
226}
227
228void CheatsModel::addSet(mCheatSet* set) {
229 beginInsertRows(QModelIndex(), mCheatSetsSize(&m_device->cheats), mCheatSetsSize(&m_device->cheats));
230 size_t size = mCheatSetsSize(&m_device->cheats);
231 if (size) {
232 set->copyProperties(set, *mCheatSetsGetPointer(&m_device->cheats, size - 1));
233 }
234 mCheatAddSet(m_device, set);
235 endInsertRows();
236}
237
238void CheatsModel::invalidated() {
239 beginResetModel();
240 endResetModel();
241}