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 int row = index.row();
46 const GBACheatSet* cheats = *GBACheatSetsGetPointer(&m_device->cheats, index.row());
47 switch (role) {
48 case Qt::DisplayRole:
49 case Qt::EditRole:
50 return cheats->name ? cheats->name : tr("(untitled)");
51 case Qt::CheckStateRole:
52 return cheats->enabled ? Qt::Checked : Qt::Unchecked;
53 default:
54 return QVariant();
55 }
56}
57
58bool CheatsModel::setData(const QModelIndex& index, const QVariant& value, int role) {
59 if (!index.isValid() || index.parent().isValid()) {
60 return false;
61 }
62
63 int row = index.row();
64 GBACheatSet* cheats = *GBACheatSetsGetPointer(&m_device->cheats, index.row());
65 switch (role) {
66 case Qt::DisplayRole:
67 case Qt::EditRole:
68 if (cheats->name) {
69 free(cheats->name);
70 cheats->name = nullptr;
71 }
72 cheats->name = strdup(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, *GBACheatSetsGetPointer(&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 GBACheatSet* cheats = static_cast<const GBACheatSet*>(index.internalPointer());
97 if (!cheats) {
98 return QModelIndex();
99 }
100 for (size_t i = 0; i < GBACheatSetsSize(&m_device->cheats); ++i) {
101 if (cheats == *GBACheatSetsGetPointer(&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 GBACheatSet* set = *GBACheatSetsGetPointer(&m_device->cheats, parent.row());
130 return StringListSize(&set->lines);
131 }
132 return GBACheatSetsSize(&m_device->cheats);
133}
134
135GBACheatSet* CheatsModel::itemAt(const QModelIndex& index) {
136 if (!index.isValid()) {
137 return nullptr;
138 }
139 if (index.parent().isValid()) {
140 return static_cast<GBACheatSet*>(index.internalPointer());
141 }
142 return *GBACheatSetsGetPointer(&m_device->cheats, index.row());
143}
144
145void CheatsModel::removeAt(const QModelIndex& index) {
146 if (!index.isValid() || index.parent().isValid()) {
147 return;
148 }
149 int row = index.row();
150 GBACheatSet* set = *GBACheatSetsGetPointer(&m_device->cheats, index.row());
151 beginRemoveRows(QModelIndex(), row, row);
152 GBACheatRemoveSet(m_device, set);
153 GBACheatSetDeinit(set);
154 delete set;
155 endInsertRows();
156}
157
158QString CheatsModel::toString(const QModelIndexList& indices) const {
159 QMap<int, GBACheatSet*> setOrder;
160 QMap<GBACheatSet*, QSet<size_t>> setIndices;
161 for (const QModelIndex& index : indices) {
162 GBACheatSet* set = static_cast<GBACheatSet*>(index.internalPointer());
163 if (!set) {
164 set = *GBACheatSetsGetPointer(&m_device->cheats, index.row());
165 setOrder[index.row()] = set;
166 QSet<size_t> range;
167 for (size_t i = 0; i < StringListSize(&set->lines); ++i) {
168 range.insert(i);
169 }
170 setIndices[set] = range;
171 } else {
172 setOrder[index.parent().row()] = set;
173 setIndices[set].insert(index.row());
174 }
175 }
176
177 QStringList strings;
178 QList<int> order = setOrder.keys();
179 std::sort(order.begin(), order.end());
180 for (int i : order) {
181 GBACheatSet* set = setOrder[i];
182 QList<size_t> indexOrdex = setIndices[set].toList();
183 std::sort(indexOrdex.begin(), indexOrdex.end());
184 for (size_t j : indexOrdex) {
185 strings.append(*StringListGetPointer(&set->lines, j));
186 }
187 }
188
189 return strings.join('\n');
190}
191
192void CheatsModel::beginAppendRow(const QModelIndex& index) {
193 if (index.parent().isValid()) {
194 beginInsertRows(index.parent(), rowCount(index.parent()), rowCount(index.parent()));
195 return;
196 }
197 beginInsertRows(index, rowCount(index), rowCount(index));
198}
199
200void CheatsModel::endAppendRow() {
201 endInsertRows();
202}
203
204void CheatsModel::loadFile(const QString& path) {
205 VFile* vf = VFileDevice::open(path, O_RDONLY);
206 if (!vf) {
207 LOG(WARN) << tr("Failed to open cheats file: %1").arg(path);
208 return;
209 }
210 beginResetModel();
211 GBACheatParseFile(m_device, vf);
212 endResetModel();
213 vf->close(vf);
214}
215
216void CheatsModel::saveFile(const QString& path) {
217 VFile* vf = VFileDevice::open(path, O_TRUNC | O_CREAT | O_WRONLY);
218 if (!vf) {
219 return;
220 }
221 GBACheatSaveFile(m_device, vf);
222 vf->close(vf);
223}
224
225void CheatsModel::addSet(GBACheatSet* set) {
226 beginInsertRows(QModelIndex(), GBACheatSetsSize(&m_device->cheats), GBACheatSetsSize(&m_device->cheats));
227 size_t size = GBACheatSetsSize(&m_device->cheats);
228 if (size) {
229 GBACheatSetCopyProperties(set, *GBACheatSetsGetPointer(&m_device->cheats, size - 1));
230 }
231 GBACheatAddSet(m_device, set);
232 endInsertRows();
233}
234
235void CheatsModel::invalidated() {
236 beginResetModel();
237 endResetModel();
238}