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 <QFont>
12#include <QSet>
13
14extern "C" {
15#include "gba/cheats.h"
16}
17
18using namespace QGBA;
19
20CheatsModel::CheatsModel(GBACheatDevice* device, QObject* parent)
21 : QAbstractItemModel(parent)
22 , m_device(device)
23{
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 GBACheatSet* cheats = static_cast<GBACheatSet*>(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 QFont("Courier New", 13);
40 default:
41 return QVariant();
42 }
43 }
44
45 if (index.row() >= GBACheatSetsSize(&m_device->cheats)) {
46 return QVariant();
47 }
48
49 int row = index.row();
50 const GBACheatSet* cheats = *GBACheatSetsGetPointer(&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() > GBACheatSetsSize(&m_device->cheats)) {
64 return false;
65 }
66
67 int row = index.row();
68 GBACheatSet* cheats = *GBACheatSetsGetPointer(&m_device->cheats, index.row());
69 switch (role) {
70 case Qt::DisplayRole:
71 case Qt::EditRole:
72 if (cheats->name) {
73 free(cheats->name);
74 cheats->name = nullptr;
75 }
76 cheats->name = strdup(value.toString().toUtf8().constData());
77 emit dataChanged(index, index);
78 return true;
79 case Qt::CheckStateRole:
80 cheats->enabled = value == Qt::Checked;
81 emit dataChanged(index, index);
82 return true;
83 default:
84 return false;
85 }
86}
87
88QModelIndex CheatsModel::index(int row, int column, const QModelIndex& parent) const {
89 if (parent.isValid()) {
90 return createIndex(row, column, *GBACheatSetsGetPointer(&m_device->cheats, parent.row()));
91 } else {
92 return createIndex(row, column, nullptr);
93 }
94}
95
96QModelIndex CheatsModel::parent(const QModelIndex& index) const {
97 if (!index.isValid()) {
98 return QModelIndex();
99 }
100 const GBACheatSet* cheats = static_cast<const GBACheatSet*>(index.internalPointer());
101 if (!cheats) {
102 return QModelIndex();
103 }
104 for (size_t i = 0; i < GBACheatSetsSize(&m_device->cheats); ++i) {
105 if (cheats == *GBACheatSetsGetPointer(&m_device->cheats, i)) {
106 return createIndex(i, 0, nullptr);
107 }
108 }
109 return QModelIndex();
110}
111
112Qt::ItemFlags CheatsModel::flags(const QModelIndex& index) const {
113 if (!index.isValid()) {
114 return 0;
115 }
116
117 if (index.parent().isValid()) {
118 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
119 }
120
121 return Qt::ItemIsUserCheckable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
122}
123
124int CheatsModel::columnCount(const QModelIndex& parent) const {
125 return 1;
126}
127
128int CheatsModel::rowCount(const QModelIndex& parent) const {
129 if (parent.isValid()) {
130 if (parent.internalPointer()) {
131 return 0;
132 }
133 const GBACheatSet* set = *GBACheatSetsGetPointer(&m_device->cheats, parent.row());
134 return StringListSize(&set->lines);
135 }
136 return GBACheatSetsSize(&m_device->cheats);
137}
138
139GBACheatSet* CheatsModel::itemAt(const QModelIndex& index) {
140 if (!index.isValid()) {
141 return nullptr;
142 }
143 if (index.parent().isValid()) {
144 return static_cast<GBACheatSet*>(index.internalPointer());
145 }
146 if (index.row() >= GBACheatSetsSize(&m_device->cheats)) {
147 return nullptr;
148 }
149 return *GBACheatSetsGetPointer(&m_device->cheats, index.row());
150}
151
152void CheatsModel::removeAt(const QModelIndex& index) {
153 if (!index.isValid() || index.parent().isValid() || index.row() >= GBACheatSetsSize(&m_device->cheats)) {
154 return;
155 }
156 int row = index.row();
157 GBACheatSet* set = *GBACheatSetsGetPointer(&m_device->cheats, index.row());
158 beginRemoveRows(QModelIndex(), row, row);
159 GBACheatRemoveSet(m_device, set);
160 GBACheatSetDeinit(set);
161 delete set;
162 endInsertRows();
163}
164
165QString CheatsModel::toString(const QModelIndexList& indices) const {
166 QMap<int, GBACheatSet*> setOrder;
167 QMap<GBACheatSet*, QSet<size_t>> setIndices;
168 for (const QModelIndex& index : indices) {
169 GBACheatSet* set = static_cast<GBACheatSet*>(index.internalPointer());
170 if (!set) {
171 set = *GBACheatSetsGetPointer(&m_device->cheats, index.row());
172 setOrder[index.row()] = set;
173 QSet<size_t> range;
174 for (size_t i = 0; i < StringListSize(&set->lines); ++i) {
175 range.insert(i);
176 }
177 setIndices[set] = range;
178 } else {
179 setOrder[index.parent().row()] = set;
180 setIndices[set].insert(index.row());
181 }
182 }
183
184 QStringList strings;
185 QList<int> order = setOrder.keys();
186 std::sort(order.begin(), order.end());
187 for (int i : order) {
188 GBACheatSet* set = setOrder[i];
189 QList<size_t> indexOrdex = setIndices[set].toList();
190 std::sort(indexOrdex.begin(), indexOrdex.end());
191 for (size_t j : indexOrdex) {
192 strings.append(*StringListGetPointer(&set->lines, j));
193 }
194 }
195
196 return strings.join('\n');
197}
198
199void CheatsModel::beginAppendRow(const QModelIndex& index) {
200 if (index.parent().isValid()) {
201 beginInsertRows(index.parent(), rowCount(index.parent()), rowCount(index.parent()));
202 return;
203 }
204 beginInsertRows(index, rowCount(index), rowCount(index));
205}
206
207void CheatsModel::endAppendRow() {
208 endInsertRows();
209}
210
211void CheatsModel::loadFile(const QString& path) {
212 VFile* vf = VFileDevice::open(path, O_RDONLY);
213 if (!vf) {
214 LOG(WARN) << tr("Failed to open cheats file: %1").arg(path);
215 return;
216 }
217 beginResetModel();
218 GBACheatParseFile(m_device, vf);
219 endResetModel();
220 vf->close(vf);
221}
222
223void CheatsModel::saveFile(const QString& path) {
224 VFile* vf = VFileDevice::open(path, O_TRUNC | O_CREAT | O_WRONLY);
225 if (!vf) {
226 return;
227 }
228 GBACheatSaveFile(m_device, vf);
229 vf->close(vf);
230}
231
232void CheatsModel::addSet(GBACheatSet* set) {
233 beginInsertRows(QModelIndex(), GBACheatSetsSize(&m_device->cheats), GBACheatSetsSize(&m_device->cheats));
234 size_t size = GBACheatSetsSize(&m_device->cheats);
235 if (size) {
236 GBACheatSetCopyProperties(set, *GBACheatSetsGetPointer(&m_device->cheats, size - 1));
237 }
238 GBACheatAddSet(m_device, set);
239 endInsertRows();
240}
241
242void CheatsModel::invalidated() {
243 beginResetModel();
244 endResetModel();
245}