1 /*
2 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6 #include "Wt/WEvent.h"
7 #include "Wt/WModelIndex.h"
8 #include "Wt/WAbstractItemModel.h"
9 #include "Wt/WItemSelectionModel.h"
10 #include "Wt/WLogger.h"
11
12 #include "WebUtils.h"
13
14 #ifdef WT_WIN32
15 #define snprintf _snprintf
16 #endif
17
18 namespace {
19 const char *DRAG_DROP_MIME_TYPE = "application/x-wabstractitemmodelselection";
20 }
21
22 namespace Wt {
23
24 LOGGER("WAbstractItemModel");
25
WAbstractItemModel()26 WAbstractItemModel::WAbstractItemModel()
27 { }
28
~WAbstractItemModel()29 WAbstractItemModel::~WAbstractItemModel()
30 { }
31
canFetchMore(const WModelIndex & parent)32 bool WAbstractItemModel::canFetchMore(const WModelIndex& parent) const
33 {
34 return false;
35 }
36
fetchMore(const WModelIndex & parent)37 void WAbstractItemModel::fetchMore(const WModelIndex& parent)
38 { }
39
flags(const WModelIndex & index)40 WFlags<ItemFlag> WAbstractItemModel::flags(const WModelIndex& index) const
41 {
42 return ItemFlag::Selectable;
43 }
44
headerFlags(int section,Orientation orientation)45 WFlags<HeaderFlag> WAbstractItemModel::headerFlags(int section,
46 Orientation orientation)
47 const
48 {
49 return None;
50 }
51
hasChildren(const WModelIndex & index)52 bool WAbstractItemModel::hasChildren(const WModelIndex& index) const
53 {
54 return rowCount(index) > 0 && columnCount(index) > 0;
55 }
56
hasIndex(int row,int column,const WModelIndex & parent)57 bool WAbstractItemModel::hasIndex(int row, int column,
58 const WModelIndex& parent) const
59 {
60 return (row >= 0
61 && column >= 0
62 && row < rowCount(parent)
63 && column < columnCount(parent));
64 }
65
66 WAbstractItemModel::DataMap
itemData(const WModelIndex & index)67 WAbstractItemModel::itemData(const WModelIndex& index) const
68 {
69 DataMap result;
70
71 if (index.isValid()) {
72 #ifndef WT_TARGET_JAVA
73 for (int i = 0; i <= ItemDataRole::BarBrushColor; ++i)
74 #else
75 for (int i = 0; i <= ItemDataRole::BarBrushColor.value(); ++i)
76 #endif
77 result[ItemDataRole(i)] = data(index, ItemDataRole(i));
78 result[ItemDataRole::User] = data(index, ItemDataRole::User);
79 }
80
81 return result;
82 }
83
data(int row,int column,ItemDataRole role,const WModelIndex & parent)84 cpp17::any WAbstractItemModel::data(int row, int column, ItemDataRole role,
85 const WModelIndex& parent) const
86 {
87 return data(index(row, column, parent), role);
88 }
89
headerData(int section,Orientation orientation,ItemDataRole role)90 cpp17::any WAbstractItemModel::headerData(int section, Orientation orientation,
91 ItemDataRole role) const
92 {
93 if (role == ItemDataRole::Level)
94 return cpp17::any((int)0);
95 else
96 return cpp17::any();
97 }
98
sort(int column,SortOrder order)99 void WAbstractItemModel::sort(int column, SortOrder order)
100 { }
101
expandColumn(int column)102 void WAbstractItemModel::expandColumn(int column)
103 { }
104
collapseColumn(int column)105 void WAbstractItemModel::collapseColumn(int column)
106 { }
107
insertColumns(int column,int count,const WModelIndex & parent)108 bool WAbstractItemModel::insertColumns(int column, int count,
109 const WModelIndex& parent)
110 {
111 return false;
112 }
113
insertRows(int row,int count,const WModelIndex & parent)114 bool WAbstractItemModel::insertRows(int row, int count,
115 const WModelIndex& parent)
116 {
117 return false;
118 }
119
removeColumns(int column,int count,const WModelIndex & parent)120 bool WAbstractItemModel::removeColumns(int column, int count,
121 const WModelIndex& parent)
122 {
123 return false;
124 }
125
removeRows(int row,int count,const WModelIndex & parent)126 bool WAbstractItemModel::removeRows(int row, int count,
127 const WModelIndex& parent)
128 {
129 return false;
130 }
131
setData(const WModelIndex & index,const cpp17::any & value,ItemDataRole role)132 bool WAbstractItemModel::setData(const WModelIndex& index,
133 const cpp17::any& value, ItemDataRole role)
134 {
135 return false;
136 }
137
setHeaderData(int section,Orientation orientation,const cpp17::any & value,ItemDataRole role)138 bool WAbstractItemModel::setHeaderData(int section, Orientation orientation,
139 const cpp17::any& value, ItemDataRole role)
140 {
141 return false;
142 }
143
setHeaderData(int section,const cpp17::any & value)144 bool WAbstractItemModel::setHeaderData(int section, const cpp17::any& value)
145 {
146 return setHeaderData(section, Orientation::Horizontal, value);
147 }
148
setItemData(const WModelIndex & index,const DataMap & values)149 bool WAbstractItemModel::setItemData(const WModelIndex& index,
150 const DataMap& values)
151 {
152 bool result = true;
153
154 for (DataMap::const_iterator i = values.begin(); i != values.end(); ++i)
155 // if (i->first != ItemDataRole::Edit)
156 if (!setData(index, i->second, i->first))
157 result = false;
158
159 dataChanged().emit(index, index);
160
161 return result;
162 }
163
insertColumn(int column,const WModelIndex & parent)164 bool WAbstractItemModel::insertColumn(int column, const WModelIndex& parent)
165 {
166 return insertColumns(column, 1, parent);
167 }
168
insertRow(int row,const WModelIndex & parent)169 bool WAbstractItemModel::insertRow(int row, const WModelIndex& parent)
170 {
171 return insertRows(row, 1, parent);
172 }
173
removeColumn(int column,const WModelIndex & parent)174 bool WAbstractItemModel::removeColumn(int column, const WModelIndex& parent)
175 {
176 return removeColumns(column, 1, parent);
177 }
178
removeRow(int row,const WModelIndex & parent)179 bool WAbstractItemModel::removeRow(int row, const WModelIndex& parent)
180 {
181 return removeRows(row, 1, parent);
182 }
183
setData(int row,int column,const cpp17::any & value,ItemDataRole role,const WModelIndex & parent)184 bool WAbstractItemModel::setData(int row, int column, const cpp17::any& value,
185 ItemDataRole role, const WModelIndex& parent)
186 {
187 WModelIndex i = index(row, column, parent);
188
189 if (i.isValid())
190 return setData(i, value, role);
191 else
192 return false;
193 }
194
reset()195 void WAbstractItemModel::reset()
196 {
197 modelReset_.emit();
198 }
199
createIndex(int row,int column,void * ptr)200 WModelIndex WAbstractItemModel::createIndex(int row, int column, void *ptr)
201 const
202 {
203 return WModelIndex(row, column, this, ptr);
204 }
205
createIndex(int row,int column,::uint64_t id)206 WModelIndex WAbstractItemModel::createIndex(int row, int column, ::uint64_t id)
207 const
208 {
209 return WModelIndex(row, column, this, id);
210 }
211
toRawIndex(const WModelIndex & index)212 void *WAbstractItemModel::toRawIndex(const WModelIndex& index) const
213 {
214 return nullptr;
215 }
216
fromRawIndex(void * rawIndex)217 WModelIndex WAbstractItemModel::fromRawIndex(void *rawIndex) const
218 {
219 return WModelIndex();
220 }
221
mimeType()222 std::string WAbstractItemModel::mimeType() const
223 {
224 return DRAG_DROP_MIME_TYPE;
225 }
226
acceptDropMimeTypes()227 std::vector<std::string> WAbstractItemModel::acceptDropMimeTypes() const
228 {
229 std::vector<std::string> result;
230
231 result.push_back(DRAG_DROP_MIME_TYPE);
232
233 return result;
234 }
235
copyData(const WAbstractItemModel * source,const WModelIndex & sIndex,WAbstractItemModel * destination,const WModelIndex & dIndex)236 void WAbstractItemModel::copyData(const WAbstractItemModel *source,
237 const WModelIndex& sIndex,
238 WAbstractItemModel *destination,
239 const WModelIndex& dIndex)
240 {
241 DataMap values = destination->itemData(dIndex);
242 for (DataMap::const_iterator i = values.begin(); i != values.end(); ++i)
243 destination->setData(dIndex, cpp17::any(), i->first);
244
245 destination->setItemData(dIndex, source->itemData(sIndex));
246 }
247
dropEvent(const WDropEvent & e,DropAction action,int row,int column,const WModelIndex & parent)248 void WAbstractItemModel::dropEvent(const WDropEvent& e, DropAction action,
249 int row, int column,
250 const WModelIndex& parent)
251 {
252 // TODO: For now, we assumes selectionBehavior() == RowSelection !
253
254 WItemSelectionModel *selectionModel
255 = dynamic_cast<WItemSelectionModel *>(e.source());
256 if (selectionModel) {
257 auto sourceModel = selectionModel->model();
258
259 /*
260 * (1) Insert new rows (or later: cells ?)
261 */
262 if (action == DropAction::Move || row == -1) {
263 if (row == -1)
264 row = rowCount(parent);
265
266 if (!insertRows(row, selectionModel->selectedIndexes().size(), parent)) {
267 LOG_ERROR("dropEvent(): could not insertRows()");
268 return;
269 }
270 }
271
272 /*
273 * (2) Copy data
274 */
275 WModelIndexSet selection = selectionModel->selectedIndexes();
276
277 int r = row;
278 for (WModelIndexSet::const_iterator i = selection.begin();
279 i != selection.end(); ++i) {
280 WModelIndex sourceIndex = *i;
281 if (selectionModel->selectionBehavior() ==
282 SelectionBehavior::Rows) {
283 WModelIndex sourceParent = sourceIndex.parent();
284
285 for (int col = 0; col < sourceModel->columnCount(sourceParent); ++col) {
286 WModelIndex s = sourceModel->index(sourceIndex.row(), col,
287 sourceParent);
288 WModelIndex d = index(r, col, parent);
289 copyData(sourceModel.get(), s, this, d);
290 }
291
292 ++r;
293 }
294 }
295
296 /*
297 * (3) Remove original data
298 */
299 if (action == DropAction::Move) {
300 while (!selectionModel->selectedIndexes().empty()) {
301 WModelIndex i = Utils::last(selectionModel->selectedIndexes());
302
303 if (!sourceModel->removeRow(i.row(), i.parent())) {
304 LOG_ERROR("dropEvent(): could not removeRows()");
305 return;
306 }
307 }
308 }
309 }
310 }
311
dropEvent(const WDropEvent & e,DropAction action,const WModelIndex & pindex,Wt::Side side)312 void WAbstractItemModel::dropEvent(const WDropEvent& e, DropAction action,
313 const WModelIndex& pindex, Wt::Side side)
314 {
315 WItemSelectionModel *selectionModel
316 = dynamic_cast<WItemSelectionModel *>(e.source());
317 if (selectionModel) {
318 auto sourceModel = selectionModel->model();
319
320 const WModelIndex& parent = pindex.parent();
321 int row = !pindex.isValid() ? rowCount() :
322 side == Side::Bottom ? pindex.row()+1 : pindex.row();
323
324 /*
325 * (1) Insert new rows (or later: cells ?)
326 */
327 if (!insertRows(row, selectionModel->selectedIndexes().size(), parent)) {
328 LOG_ERROR("dropEvent(): could not insertRows()");
329 return;
330 }
331
332 /*
333 * (2) Copy data
334 */
335 WModelIndexSet selection = selectionModel->selectedIndexes();
336
337 int r = row;
338 for (WModelIndexSet::const_iterator i = selection.begin();
339 i != selection.end(); ++i) {
340 WModelIndex sourceIndex = *i;
341 if (selectionModel->selectionBehavior() ==
342 SelectionBehavior::Rows) {
343 WModelIndex sourceParent = sourceIndex.parent();
344
345 for (int col = 0; col < sourceModel->columnCount(sourceParent); ++col) {
346 WModelIndex s = sourceModel->index(sourceIndex.row(), col,
347 sourceParent);
348 WModelIndex d = index(r, col, parent);
349 copyData(sourceModel.get(), s, this, d);
350 }
351
352 ++r;
353 }
354 }
355
356 /*
357 * (3) Remove original data
358 */
359 if (action == DropAction::Move) {
360 while (!selectionModel->selectedIndexes().empty()) {
361 WModelIndex i = Utils::last(selectionModel->selectedIndexes());
362
363 if (!sourceModel->removeRow(i.row(), i.parent())) {
364 LOG_ERROR("dropEvent(): could not removeRows()");
365 return;
366 }
367 }
368 }
369 }
370 }
371
beginInsertColumns(const WModelIndex & parent,int first,int last)372 void WAbstractItemModel::beginInsertColumns(const WModelIndex& parent,
373 int first, int last)
374 {
375 first_ = first;
376 last_ = last;
377 parent_ = parent;
378
379 columnsAboutToBeInserted().emit(parent_, first, last);
380 }
381
endInsertColumns()382 void WAbstractItemModel::endInsertColumns()
383 {
384 columnsInserted().emit(parent_, first_, last_);
385 }
386
beginInsertRows(const WModelIndex & parent,int first,int last)387 void WAbstractItemModel::beginInsertRows(const WModelIndex& parent,
388 int first, int last)
389 {
390 first_ = first;
391 last_ = last;
392 parent_ = parent;
393
394 rowsAboutToBeInserted().emit(parent, first, last);
395 }
396
endInsertRows()397 void WAbstractItemModel::endInsertRows()
398 {
399 rowsInserted().emit(parent_, first_, last_);
400 }
401
beginRemoveColumns(const WModelIndex & parent,int first,int last)402 void WAbstractItemModel::beginRemoveColumns(const WModelIndex& parent,
403 int first, int last)
404 {
405 first_ = first;
406 last_ = last;
407 parent_ = parent;
408
409 columnsAboutToBeRemoved().emit(parent, first, last);
410 }
411
endRemoveColumns()412 void WAbstractItemModel::endRemoveColumns()
413 {
414 columnsRemoved().emit(parent_, first_, last_);
415 }
416
beginRemoveRows(const WModelIndex & parent,int first,int last)417 void WAbstractItemModel::beginRemoveRows(const WModelIndex& parent,
418 int first, int last)
419 {
420 first_ = first;
421 last_ = last;
422 parent_ = parent;
423
424 rowsAboutToBeRemoved().emit(parent, first, last);
425 }
426
endRemoveRows()427 void WAbstractItemModel::endRemoveRows()
428 {
429 rowsRemoved().emit(parent_, first_, last_);
430 }
431
match(const WModelIndex & start,ItemDataRole role,const cpp17::any & value,int hits,WFlags<MatchFlag> flags)432 WModelIndexList WAbstractItemModel::match(const WModelIndex& start,
433 ItemDataRole role,
434 const cpp17::any& value,
435 int hits,
436 WFlags<MatchFlag> flags)
437 const
438 {
439 WModelIndexList result;
440
441 const int rc = rowCount(start.parent());
442
443 for (int i = 0; i < rc; ++i) {
444 int row = start.row() + i;
445
446 if (row >= rc) {
447 if (!(flags & MatchFlag::Wrap))
448 break;
449 else
450 row -= rc;
451 }
452
453 WModelIndex idx = index(row, start.column(), start.parent());
454 cpp17::any v = data(idx, role);
455
456 if (Impl::matchValue(v, value, flags)) {
457 result.push_back(idx);
458 if (hits != -1 && (int)result.size() == hits)
459 break;
460 }
461 }
462
463 return result;
464 }
465
466 }
467