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