1 /*
2 * wangsetmodel.cpp
3 * Copyright 2017, Benjamin Trotter <bdtrotte@ucsc.edu>
4 *
5 * This file is part of Tiled.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "wangsetmodel.h"
22
23 #include "changeevents.h"
24 #include "containerhelpers.h"
25 #include "map.h"
26 #include "mapdocument.h"
27 #include "tile.h"
28 #include "tileset.h"
29 #include "tilesetdocument.h"
30 #include "tilesetdocumentsmodel.h"
31 #include "wangoverlay.h"
32 #include "wangset.h"
33
34 #include <QApplication>
35 #include <QFont>
36 #include <QPalette>
37
38 using namespace Tiled;
39
WangSetModel(QAbstractItemModel * tilesetDocumentModel,QObject * parent)40 WangSetModel::WangSetModel(QAbstractItemModel *tilesetDocumentModel,
41 QObject *parent):
42 QAbstractItemModel(parent),
43 mTilesetDocumentsModel(tilesetDocumentModel)
44 {
45 connect(mTilesetDocumentsModel, &QAbstractItemModel::rowsInserted,
46 this, &WangSetModel::onTilesetRowsInserted);
47 connect(mTilesetDocumentsModel, &QAbstractItemModel::rowsAboutToBeRemoved,
48 this, &WangSetModel::onTilesetRowsAboutToBeRemoved);
49 connect(mTilesetDocumentsModel, &QAbstractItemModel::rowsMoved,
50 this, &WangSetModel::onTilesetRowsMoved);
51 connect(mTilesetDocumentsModel, &QAbstractItemModel::layoutChanged,
52 this, &WangSetModel::onTilesetLayoutChanged);
53 connect(mTilesetDocumentsModel, &QAbstractItemModel::dataChanged,
54 this, &WangSetModel::onTilesetDataChanged);
55 }
56
~WangSetModel()57 WangSetModel::~WangSetModel()
58 {
59 }
60
index(int row,int column,const QModelIndex & parent) const61 QModelIndex WangSetModel::index(int row, int column, const QModelIndex &parent) const
62 {
63 if (!hasIndex(row, column, parent))
64 return QModelIndex();
65
66 if (!parent.isValid())
67 return createIndex(row, column);
68 else if (Tileset *tileset = tilesetAt(parent))
69 return createIndex(row, column, tileset);
70
71 return QModelIndex();
72 }
73
index(Tileset * tileset) const74 QModelIndex WangSetModel::index(Tileset *tileset) const
75 {
76 for (int row = 0; row < mTilesetDocuments.size(); ++row)
77 if (mTilesetDocuments.at(row)->tileset() == tileset)
78 return createIndex(row, 0);
79
80 return QModelIndex();
81 }
82
index(WangSet * wangSet) const83 QModelIndex WangSetModel::index(WangSet *wangSet) const
84 {
85 Tileset *tileset = wangSet->tileset();
86 int row = tileset->wangSets().indexOf(wangSet);
87 return createIndex(row, 0, tileset);
88 }
89
parent(const QModelIndex & child) const90 QModelIndex WangSetModel::parent(const QModelIndex &child) const
91 {
92 if (WangSet *wangSet = wangSetAt(child))
93 return index(wangSet->tileset());
94
95 return QModelIndex();
96 }
97
rowCount(const QModelIndex & parent) const98 int WangSetModel::rowCount(const QModelIndex &parent) const
99 {
100 if (!parent.isValid())
101 return mTilesetDocuments.size();
102 else if (Tileset *tileset = tilesetAt(parent))
103 return tileset->wangSetCount();
104
105 return 0;
106 }
107
columnCount(const QModelIndex & parent) const108 int WangSetModel::columnCount(const QModelIndex &parent) const
109 {
110 Q_UNUSED(parent)
111 return 1;
112 }
113
data(const QModelIndex & index,int role) const114 QVariant WangSetModel::data(const QModelIndex &index, int role) const
115 {
116 if (WangSet *wangSet = wangSetAt(index)) {
117 switch (role) {
118 case Qt::DisplayRole:
119 case Qt::EditRole:
120 return wangSet->name();
121 case Qt::DecorationRole:
122 if (Tile *tile = wangSet->imageTile())
123 return tile->image();
124 else
125 return wangSetIcon(wangSet->type());
126 break;
127 case WangSetRole:
128 return QVariant::fromValue(wangSet);
129 }
130 } else if (Tileset *tileset = tilesetAt(index)) {
131 switch (role) {
132 case Qt::DisplayRole:
133 return tileset->name();
134 case Qt::SizeHintRole:
135 return QSize(1, 32);
136 case Qt::FontRole: {
137 QFont font = QApplication::font();
138 font.setBold(true);
139 return font;
140 }
141 case Qt::BackgroundRole: {
142 QColor bg = QApplication::palette().alternateBase().color();
143 return bg;//.darker(103);
144 }
145 }
146 }
147
148 return QVariant();
149 }
150
flags(const QModelIndex & index) const151 Qt::ItemFlags WangSetModel::flags(const QModelIndex &index) const
152 {
153 Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
154
155 if (tilesetAt(index))
156 defaultFlags &= ~Qt::ItemIsSelectable;
157
158 return defaultFlags;
159 }
160
tilesetAt(const QModelIndex & index) const161 Tileset *WangSetModel::tilesetAt(const QModelIndex &index) const
162 {
163 if (!index.isValid())
164 return nullptr;
165 if (index.parent().isValid()) // tilesets don't have parents
166 return nullptr;
167 if (index.row() >= mTilesetDocuments.size())
168 return nullptr;
169
170 return mTilesetDocuments.at(index.row())->tileset().data();
171 }
172
wangSetAt(const QModelIndex & index) const173 WangSet *WangSetModel::wangSetAt(const QModelIndex &index) const
174 {
175 if (!index.isValid())
176 return nullptr;
177
178 if (Tileset *tileset = static_cast<Tileset*>(index.internalPointer()))
179 return tileset->wangSet(index.row());
180
181 return nullptr;
182 }
183
onTilesetRowsInserted(const QModelIndex & parent,int first,int last)184 void WangSetModel::onTilesetRowsInserted(const QModelIndex &parent, int first, int last)
185 {
186 beginInsertRows(QModelIndex(), first, last);
187 for (int row = first; row <= last; ++row) {
188 const QModelIndex index = mTilesetDocumentsModel->index(row, 0, parent);
189 const QVariant var = mTilesetDocumentsModel->data(index, TilesetDocumentsModel::TilesetDocumentRole);
190 TilesetDocument *tilesetDocument = var.value<TilesetDocument*>();
191
192 mTilesetDocuments.insert(row, tilesetDocument);
193
194 connect(tilesetDocument, &Document::changed, this, &WangSetModel::onDocumentChanged);
195 }
196 endInsertRows();
197 }
198
onTilesetRowsAboutToBeRemoved(const QModelIndex & parent,int first,int last)199 void WangSetModel::onTilesetRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
200 {
201 Q_UNUSED(parent)
202
203 beginRemoveRows(QModelIndex(), first, last);
204 for (int index = last; index >= first; --index) {
205 TilesetDocument *tilesetDocument = mTilesetDocuments.takeAt(index);
206 tilesetDocument->disconnect(this);
207 }
208 endRemoveRows();
209 }
210
onTilesetRowsMoved(const QModelIndex & parent,int start,int end,const QModelIndex & destination,int row)211 void WangSetModel::onTilesetRowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
212 {
213 Q_UNUSED(parent)
214 Q_UNUSED(destination)
215
216 beginMoveRows(QModelIndex(), start, end, QModelIndex(), row);
217
218 if (start == row)
219 return;
220
221 while (start <= end) {
222 mTilesetDocuments.move(start, row);
223
224 if (row < start) {
225 ++start;
226 ++row;
227 } else {
228 --end;
229 }
230 }
231
232 endMoveRows();
233 }
234
onTilesetLayoutChanged(const QList<QPersistentModelIndex> & parents,QAbstractItemModel::LayoutChangeHint hint)235 void WangSetModel::onTilesetLayoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint)
236 {
237 Q_UNUSED(parents)
238 Q_UNUSED(hint)
239
240 // Make sure the tileset documents are still in the right order
241 for (int i = 0, rows = mTilesetDocuments.size(); i < rows; ++i) {
242 const QModelIndex index = mTilesetDocumentsModel->index(i, 0);
243 const QVariant var = mTilesetDocumentsModel->data(index, TilesetDocumentsModel::TilesetDocumentRole);
244 TilesetDocument *tilesetDocument = var.value<TilesetDocument*>();
245 int currentIndex = mTilesetDocuments.indexOf(tilesetDocument);
246 if (currentIndex != i) {
247 Q_ASSERT(currentIndex > i);
248 onTilesetRowsMoved(QModelIndex(), currentIndex, currentIndex, QModelIndex(), i);
249 }
250 }
251 }
252
onTilesetDataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight)253 void WangSetModel::onTilesetDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
254 {
255 emit dataChanged(index(topLeft.row(), topLeft.column()),
256 index(bottomRight.row(), bottomRight.column()));
257 }
258
onDocumentChanged(const ChangeEvent & change)259 void WangSetModel::onDocumentChanged(const ChangeEvent &change)
260 {
261 switch (change.type) {
262 case ChangeEvent::WangSetAboutToBeAdded: {
263 auto wangSetEvent = static_cast<const WangSetEvent&>(change);
264
265 QModelIndex parent = this->index(wangSetEvent.tileset);
266 beginInsertRows(parent, wangSetEvent.index, wangSetEvent.index);
267 break;
268 }
269 case ChangeEvent::WangSetAdded: {
270 auto wangSetEvent = static_cast<const WangSetEvent&>(change);
271
272 endInsertRows();
273
274 const QModelIndex index = WangSetModel::index(wangSetEvent.tileset);
275 emit dataChanged(index, index);
276 break;
277 }
278
279 case ChangeEvent::WangSetAboutToBeRemoved: {
280 auto wangSetEvent = static_cast<const WangSetEvent&>(change);
281 auto wangSet = wangSetEvent.tileset->wangSet(wangSetEvent.index);
282
283 QModelIndex parent = index(wangSetEvent.tileset);
284 beginRemoveRows(parent, index(wangSet).row(), index(wangSet).row());
285 break;
286 }
287 case ChangeEvent::WangSetRemoved: {
288 auto wangSetEvent = static_cast<const WangSetEvent&>(change);
289
290 endRemoveRows();
291
292 const QModelIndex index = WangSetModel::index(wangSetEvent.tileset);
293 emit dataChanged(index, index);
294 break;
295 }
296 default:
297 break;
298 }
299 }
300
301 #include "moc_wangsetmodel.cpp"
302