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