Crossfire Resources Editor
AssetModel.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 2022 the Crossfire Development Team
5  *
6  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
7  * welcome to redistribute it under certain conditions. For details, please
8  * see COPYING and LICENSE.
9  *
10  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
11  */
12 
13 #include "AssetModel.h"
14 #include "AssetWrapper.h"
15 #include <QScriptEngine>
16 #include <QMimeData>
17 
18 AssetModel::AssetModel(AssetWrapper *assets, QObject *parent) : QAbstractItemModel(parent) {
19  myAssets = assets;
20  connect(myAssets, SIGNAL(dataModified(AssetWrapper *, AssetWrapper::ChangeType, int)), this, SLOT(assetModified(AssetWrapper *, AssetWrapper::ChangeType, int)));
21 }
22 
24 }
25 
26 int AssetModel::columnCount(const QModelIndex &) const {
27  return 1;
28 }
29 
30 QModelIndex AssetModel::index(int row, int column, const QModelIndex& parent) const {
31  if (column != 0) {
32  return QModelIndex();
33  }
34 
35  if (parent.isValid()) {
36  AssetWrapper *wrapper = static_cast<AssetWrapper *>(parent.internalPointer());
37  return createIndex(row, column, wrapper->child(row));
38  }
39 
40  if (row < 0 || row >= myAssets->childrenCount()) {
41  return QModelIndex();
42  }
43  auto child = myAssets->child(row);
44  return createIndex(row, column, child);
45 }
46 
47 QModelIndex AssetModel::parent(const QModelIndex& index) const {
48  if (!index.isValid() || index.column() != 0) {
49  return QModelIndex();
50  }
51 
52  auto item = static_cast<AssetWrapper *>(index.internalPointer());
53  if (!item || !item->displayParent()) {
54  return QModelIndex();
55  }
56  auto parent = item->displayParent();
57  if (!parent->displayParent()) {
58  return QModelIndex();
59  }
60  return createIndex(parent->displayParent()->childIndex(parent), 0, parent);
61 }
62 
63 int AssetModel::rowCount(const QModelIndex &parent) const {
64  if (parent.isValid()) {
65  return static_cast<AssetWrapper *>(parent.internalPointer())->childrenCount();
66  }
67  return myAssets->childrenCount();
68 }
69 
70 QVariant AssetModel::data(const QModelIndex& index, int role) const {
71  if (!index.isValid() || !index.internalPointer()) {
72  return QVariant();
73  }
74 
75  if (Qt::DisplayRole != role && Qt::DecorationRole != role && Qt::ToolTipRole != role) {
76  return QVariant();
77  }
78 
79  auto wrapper = static_cast<const AssetWrapper *>(index.internalPointer());
80  if (Qt::DecorationRole == role) {
81  return wrapper->displayIcon();
82  }
83  if (Qt::ToolTipRole == role) {
84  return wrapper->tooltip();
85  }
86  return wrapper->displayName();
87 }
88 
89 QVariant AssetModel::headerData(int section, Qt::Orientation orientation, int role) const {
90  (void)orientation;
91  (void)role;
92  switch(section)
93  {
94  case 0:
95  return tr("Asset");
96  }
97 
98  return QVariant();
99 }
100 
101 Qt::ItemFlags AssetModel::flags(const QModelIndex &index) const {
102  auto flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled;
103  if (index.isValid() && index.internalPointer()) {
104  auto wrapper = static_cast<AssetWrapper *>(index.internalPointer());
105  if (wrapper->canDrag()) {
106  flags |= Qt::ItemIsDragEnabled;
107  }
108  }
109  return flags;
110 }
111 
112 QMimeData *AssetModel::mimeData(const QModelIndexList &indexes) const {
113  QMimeData *data = nullptr;
114  for (auto index : indexes) {
115  if (index.isValid() && index.internalPointer()) {
116  auto wrapper = static_cast<AssetWrapper *>(index.internalPointer());
117  if (wrapper->canDrag()) {
118  if (!data) {
119  data = new QMimeData();
120  }
121  wrapper->drag(data);
122  }
123  }
124  }
125  return data;
126 }
127 
128 bool AssetModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const {
129  (void)column;
130  if (action != Qt::CopyAction) {
131  return false;
132  }
133 
134  if (parent.isValid() && parent.internalPointer()) {
135  auto wrapper = static_cast<AssetWrapper *>(parent.internalPointer());
136  return wrapper->canDrop(data, row);
137  }
138  return false;
139 }
140 
141 bool AssetModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
142  (void)column;
143  if (action != Qt::CopyAction) {
144  return false;
145  }
146 
147  if (parent.isValid() && parent.internalPointer()) {
148  auto wrapper = static_cast<AssetWrapper *>(parent.internalPointer());
149  wrapper->drop(data, row);
150  return true;
151  }
152  return false;
153 }
154 
156  auto parent = asset->displayParent();
157  if (!parent) {
158  emit dataChanged(QModelIndex(), QModelIndex());
159  return;
160  }
161 
162  auto idx = createIndex(parent->childIndex(asset), 0, asset);
163  switch (type) {
165  emit dataChanged(idx, idx);
166  if (extra > 0) {
167  int count = asset->childrenCount() - 1;
168  auto left = createIndex(0, 0, asset->child(0));
169  auto right = createIndex(count, 0, asset->child(count));
170  emit dataChanged(left, right);
171  }
172  break;
173  }
175  beginInsertRows(idx, extra, extra);
176  break;
177  }
179  endInsertRows();
180  break;
181  }
183  beginRemoveRows(idx, extra, extra);
184  break;
185  }
187  endRemoveRows();
188  break;
189  }
191  emit layoutAboutToBeChanged();
192  break;
193  }
195  emit layoutChanged();
196  break;
197  }
198  }
199 }
200 
201 UseFilterAssetModel::UseFilterAssetModel(QObject *parent) : QSortFilterProxyModel(parent), myAsset(nullptr) {
202 };
203 
205  myAsset = asset;
206  myCachedFilter.clear();
207  myCachedHints.clear();
208  invalidateFilter();
209 }
210 
211 bool UseFilterAssetModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
212  if (!myAsset) {
213  return true;
214  }
215 
216  auto index = sourceModel()->index(sourceRow, 0, sourceParent);
217  auto wrapper = qobject_cast<AssetWrapper *>(static_cast<QObject *>(index.internalPointer()));
218  if (!wrapper) {
219  return false;
220  }
221 
222  auto cache = myCachedFilter.find(wrapper);
223  if (cache != myCachedFilter.end()) {
224  return cache->second;
225  }
226 
227  bool uses = false;
228  std::string hint;
229  auto possibleUse = wrapper->uses(myAsset, hint);
230  if (AssetWrapper::Uses == possibleUse) {
231  uses = true;
232  if (!hint.empty()) {
233  myCachedHints[wrapper] = hint;
234  }
235  } else if (AssetWrapper::ChildrenMayUse == possibleUse) {
236  int count = sourceModel()->rowCount(index);
237  for (int c = 0; c < count && !uses; c++) {
238  uses |= filterAcceptsRow(c, index);
239  }
240  }
241  myCachedFilter[wrapper] = uses;
242  return uses;
243 }
244 
245 QVariant UseFilterAssetModel::data(const QModelIndex& index, int role) const {
246  QVariant ret = QSortFilterProxyModel::data(index, role);
247  if (role == Qt::DisplayRole) {
248  auto sourceIndex = mapToSource(index);
249  auto w = qobject_cast<AssetWrapper *>(static_cast<QObject *>(sourceIndex.internalPointer()));
250  auto hint = w == nullptr ? myCachedHints.end() : myCachedHints.find(w);
251  if (hint != myCachedHints.end()) {
252  return ret.toString() + QString(" (") + QString::fromLocal8Bit(hint->second.c_str()) + ")";
253  }
254  }
255  return ret;
256 }
257 
258 ScriptFilterAssetModel::ScriptFilterAssetModel(AssetModel *model, QScriptEngine *engine, QObject *parent)
259  : QSortFilterProxyModel(parent), myEngine(engine) {
260  setSourceModel(model);
261 }
262 
263 void ScriptFilterAssetModel::setFilter(const QString &filter) {
264  myFilter = filter;
265  myCachedFilter.clear();
266  invalidateFilter();
267 }
268 
269 bool ScriptFilterAssetModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
270  if (myFilter.isEmpty())
271  return true;
272 
273  auto index = sourceModel()->index(sourceRow, 0, sourceParent);
274  auto wrapper = qobject_cast<AssetWrapper *>(static_cast<QObject *>(index.internalPointer()));
275  if (!wrapper) {
276  return false;
277  }
278 
279  auto cache = myCachedFilter.find(wrapper);
280  if (cache != myCachedFilter.end()) {
281  return cache->second;
282  }
283 
284  bool accept = acceptItem(wrapper);
285  if (!accept) {
286  int count = sourceModel()->rowCount(index);
287  for (int c = 0; c < count && !accept; c++) {
288  accept |= filterAcceptsRow(c, index);
289  }
290  }
291  myCachedFilter[wrapper] = accept;
292  return accept;
293 }
294 
296  QScriptValue engineValue = myEngine->newQObject(item);
297  myEngine->globalObject().setProperty("item", engineValue);
298 
299  myEngine->pushContext();
300  bool show = myEngine->evaluate(myFilter).toBoolean();
301  myEngine->popContext();
302  if (myEngine->hasUncaughtException()) {
303  //qDebug() << myEngine->uncaughtException().toString();
304  return false;
305  }
306 
307  return show;
308 }
AssetWrapper::AfterLayoutChange
@ AfterLayoutChange
Definition: AssetWrapper.h:33
AssetWrapper.h
AssetWrapper::AssetUpdated
@ AssetUpdated
Definition: AssetWrapper.h:33
AssetWrapper::AfterChildAdd
@ AfterChildAdd
Definition: AssetWrapper.h:33
ScriptFilterAssetModel::filterAcceptsRow
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
Definition: AssetModel.cpp:269
AssetModel::flags
virtual Qt::ItemFlags flags(const QModelIndex &index) const override
Definition: AssetModel.cpp:101
ScriptFilterAssetModel::setFilter
void setFilter(const QString &filter)
Definition: AssetModel.cpp:263
UseFilterAssetModel::myAsset
AssetWrapper * myAsset
Definition: AssetModel.h:68
AssetWrapper::AfterChildRemove
@ AfterChildRemove
Definition: AssetWrapper.h:33
AssetWrapper::child
virtual AssetWrapper * child(int)
Definition: AssetWrapper.h:52
UseFilterAssetModel::filterAcceptsRow
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
Definition: AssetModel.cpp:211
AssetModel::myAssets
AssetWrapper * myAssets
Definition: AssetModel.h:52
UseFilterAssetModel::myCachedHints
std::map< AssetWrapper *, std::string > myCachedHints
Definition: AssetModel.h:70
AssetModel::canDropMimeData
virtual bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override
Definition: AssetModel.cpp:128
UseFilterAssetModel::setFilter
void setFilter(AssetWrapper *asset)
Definition: AssetModel.cpp:204
AssetWrapper::ChildrenMayUse
@ ChildrenMayUse
Definition: AssetWrapper.h:32
ScriptFilterAssetModel::acceptItem
bool acceptItem(AssetWrapper *item) const
Definition: AssetModel.cpp:295
AssetWrapper::BeforeChildAdd
@ BeforeChildAdd
Definition: AssetWrapper.h:33
AssetModel::data
virtual QVariant data(const QModelIndex &index, int role) const override
Definition: AssetModel.cpp:70
AssetWrapper::childrenCount
virtual int childrenCount() const
Definition: AssetWrapper.h:51
AssetWrapper
Base class for all assets that can be displayed or edited by CRE.
Definition: AssetWrapper.h:25
ScriptFilterAssetModel::ScriptFilterAssetModel
ScriptFilterAssetModel(AssetModel *model, QScriptEngine *engine, QObject *parent)
Definition: AssetModel.cpp:258
AssetWrapper::canDrop
virtual bool canDrop(const QMimeData *, int) const
Definition: AssetWrapper.h:64
UseFilterAssetModel::data
virtual QVariant data(const QModelIndex &index, int role) const override
Definition: AssetModel.cpp:245
AssetWrapper::ChangeType
ChangeType
Definition: AssetWrapper.h:33
AssetModel
Qt model representing all items in CRE, with the exception of experience.
Definition: AssetModel.h:29
AssetModel::columnCount
virtual int columnCount(const QModelIndex &parent) const override
Definition: AssetModel.cpp:26
UseFilterAssetModel::UseFilterAssetModel
UseFilterAssetModel(QObject *parent)
Definition: AssetModel.cpp:201
AssetModel.h
AssetModel::dropMimeData
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
Definition: AssetModel.cpp:141
AssetWrapper::Uses
@ Uses
Definition: AssetWrapper.h:32
AssetWrapper::drop
virtual void drop(const QMimeData *, int)
Definition: AssetWrapper.h:65
AssetWrapper::displayIcon
virtual QIcon displayIcon() const
Definition: AssetWrapper.h:44
AssetModel::parent
virtual QModelIndex parent(const QModelIndex &index) const override
Definition: AssetModel.cpp:47
ScriptFilterAssetModel::filter
const QString & filter() const
Definition: AssetModel.h:83
ScriptFilterAssetModel::myFilter
QString myFilter
Definition: AssetModel.h:90
AssetWrapper::BeforeLayoutChange
@ BeforeLayoutChange
Definition: AssetWrapper.h:33
AssetWrapper::BeforeChildRemove
@ BeforeChildRemove
Definition: AssetWrapper.h:33
AssetModel::~AssetModel
virtual ~AssetModel()
Definition: AssetModel.cpp:23
ScriptFilterAssetModel::myEngine
QScriptEngine * myEngine
Definition: AssetModel.h:89
AssetModel::rowCount
virtual int rowCount(const QModelIndex &parent) const override
Definition: AssetModel.cpp:63
ScriptFilterAssetModel::myCachedFilter
std::map< AssetWrapper *, bool > myCachedFilter
Definition: AssetModel.h:91
AssetModel::index
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const override
Definition: AssetModel.cpp:30
UseFilterAssetModel::myCachedFilter
std::map< AssetWrapper *, bool > myCachedFilter
Definition: AssetModel.h:69
AssetModel::assetModified
void assetModified(AssetWrapper *asset, AssetWrapper::ChangeType type, int extra)
Definition: AssetModel.cpp:155
AssetModel::AssetModel
AssetModel(AssetWrapper *assets, QObject *parent)
Definition: AssetModel.cpp:18
AssetModel::mimeData
virtual QMimeData * mimeData(const QModelIndexList &indexes) const override
Definition: AssetModel.cpp:112
AssetWrapper::displayParent
AssetWrapper * displayParent() const
Definition: AssetWrapper.h:46
AssetModel::headerData
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override
Definition: AssetModel.cpp:89