1 /*
2  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3  *
4  * See the LICENSE file for terms of use.
5  */
6 
7 #include "Wt/WStandardItem.h"
8 #include "Wt/WStandardItemModel.h"
9 #include "Wt/WEvent.h"
10 #include "Wt/WItemSelectionModel.h"
11 
12 #ifndef DOXYGEN_ONLY
13 
14 namespace Wt {
15 
WStandardItemModel()16 WStandardItemModel::WStandardItemModel()
17   : sortRole_(ItemDataRole::Display)
18 {
19   init();
20 }
21 
WStandardItemModel(int rows,int columns)22 WStandardItemModel::WStandardItemModel(int rows, int columns)
23   : sortRole_(ItemDataRole::Display)
24 {
25   init();
26 
27   invisibleRootItem_->setColumnCount(columns);
28   invisibleRootItem_->setRowCount(rows);
29 }
30 
init()31 void WStandardItemModel::init()
32 {
33   invisibleRootItem_.reset(new WStandardItem());
34   invisibleRootItem_->model_ = this;
35 
36   itemPrototype_.reset(new WStandardItem());
37 }
38 
~WStandardItemModel()39 WStandardItemModel::~WStandardItemModel()
40 { }
41 
clear()42 void WStandardItemModel::clear()
43 {
44   invisibleRootItem_->setRowCount(0);
45   invisibleRootItem_->setColumnCount(0);
46 
47   columnHeaderData_.clear();
48   rowHeaderData_.clear();
49   columnHeaderFlags_.clear();
50   rowHeaderFlags_.clear();
51 
52   reset();
53 }
54 
indexFromItem(const WStandardItem * item)55 WModelIndex WStandardItemModel::indexFromItem(const WStandardItem *item) const
56 {
57   if (item == invisibleRootItem_.get())
58     return WModelIndex();
59   else
60     return createIndex(item->row(), item->column(),
61 		       static_cast<void *>(item->parent()));
62 }
63 
itemFromIndex(const WModelIndex & index)64 WStandardItem *WStandardItemModel::itemFromIndex(const WModelIndex& index) const
65 {
66   return itemFromIndex(index, true);
67 }
68 
itemFromIndex(const WModelIndex & index,bool lazyCreate)69 WStandardItem *WStandardItemModel::itemFromIndex(const WModelIndex& index,
70 						 bool lazyCreate) const
71 {
72   if (!index.isValid())
73     return invisibleRootItem_.get();
74   else
75     if (index.model() != this)
76       return nullptr;
77     else {
78       WStandardItem *parent
79 	= static_cast<WStandardItem *>(index.internalPointer());
80       WStandardItem *c = parent->child(index.row(), index.column());
81 
82       if (lazyCreate && !c) {
83 	auto item = itemPrototype()->clone();
84 	c = item.get();
85 	parent->setChild(index.row(), index.column(), std::move(item));
86       }
87 
88       return c;
89     }
90 }
91 
92 
93 void WStandardItemModel
appendColumn(std::vector<std::unique_ptr<WStandardItem>> items)94 ::appendColumn(std::vector<std::unique_ptr<WStandardItem> > items)
95 {
96   insertColumn(columnCount(), std::move(items));
97 }
98 
99 void WStandardItemModel
insertColumn(int column,std::vector<std::unique_ptr<WStandardItem>> items)100 ::insertColumn(int column, std::vector<std::unique_ptr<WStandardItem> > items)
101 {
102   invisibleRootItem_->insertColumn(column, std::move(items));
103 }
104 
105 void WStandardItemModel
appendRow(std::vector<std::unique_ptr<WStandardItem>> items)106 ::appendRow(std::vector<std::unique_ptr<WStandardItem> > items)
107 {
108   insertRow(rowCount(), std::move(items));
109 }
110 
111 void WStandardItemModel
insertRow(int row,std::vector<std::unique_ptr<WStandardItem>> items)112 ::insertRow(int row, std::vector<std::unique_ptr<WStandardItem> > items)
113 {
114   invisibleRootItem_->insertRow(row, std::move(items));
115 }
116 
appendRow(std::unique_ptr<WStandardItem> item)117 void WStandardItemModel::appendRow(std::unique_ptr<WStandardItem> item)
118 {
119   insertRow(rowCount(), std::move(item));
120 }
121 
insertRow(int row,std::unique_ptr<WStandardItem> item)122 void WStandardItemModel::insertRow(int row,
123 				   std::unique_ptr<WStandardItem> item)
124 {
125   invisibleRootItem_->insertRow(row, std::move(item));
126 }
127 
item(int row,int column)128 WStandardItem *WStandardItemModel::item(int row, int column) const
129 {
130   return invisibleRootItem_->child(row, column);
131 }
132 
setItem(int row,int column,std::unique_ptr<WStandardItem> item)133 void WStandardItemModel::setItem(int row, int column,
134 				 std::unique_ptr<WStandardItem> item)
135 {
136   invisibleRootItem_->setChild(row, column, std::move(item));
137 }
138 
itemPrototype()139 WStandardItem *WStandardItemModel::itemPrototype() const
140 {
141   return itemPrototype_.get();
142 }
143 
144 void WStandardItemModel
setItemPrototype(std::unique_ptr<WStandardItem> item)145 ::setItemPrototype(std::unique_ptr<WStandardItem> item)
146 {
147   itemPrototype_ = std::move(item);
148 }
149 
150 std::vector<std::unique_ptr<WStandardItem> > WStandardItemModel
takeColumn(int column)151 ::takeColumn(int column)
152 {
153   return invisibleRootItem_->takeColumn(column);
154 }
155 
156 std::vector<std::unique_ptr<WStandardItem> > WStandardItemModel
takeRow(int row)157 ::takeRow(int row)
158 {
159   return invisibleRootItem_->takeRow(row);
160 }
161 
162 std::unique_ptr<WStandardItem> WStandardItemModel
takeItem(int row,int column)163 ::takeItem(int row, int column)
164 {
165   return invisibleRootItem_->takeChild(row, column);
166 }
167 
flags(const WModelIndex & index)168 WFlags<ItemFlag> WStandardItemModel::flags(const WModelIndex& index) const
169 {
170   WStandardItem *item = itemFromIndex(index, false);
171 
172   return item ? item->flags() : WFlags<ItemFlag>(None);
173 }
174 
parent(const WModelIndex & index)175 WModelIndex WStandardItemModel::parent(const WModelIndex& index) const
176 {
177   if (!index.isValid())
178     return index;
179 
180   WStandardItem *parent
181     = static_cast<WStandardItem *>(index.internalPointer());
182 
183   return indexFromItem(parent);
184 }
185 
data(const WModelIndex & index,ItemDataRole role)186 cpp17::any WStandardItemModel::data(const WModelIndex& index, ItemDataRole role) const
187 {
188   WStandardItem *item = itemFromIndex(index, false);
189 
190   return item ? item->data(role) : cpp17::any();
191 }
192 
headerData(int section,Orientation orientation,ItemDataRole role)193 cpp17::any WStandardItemModel::headerData(int section, Orientation orientation,
194                                    ItemDataRole role) const
195 {
196   if (role == ItemDataRole::Level)
197     return 0;
198 
199   const std::vector<HeaderData>& headerData
200     = (orientation == Orientation::Horizontal)
201     ? columnHeaderData_ : rowHeaderData_;
202 
203   if (section >= (int)headerData.size())
204     return cpp17::any();
205 
206   const HeaderData& d = headerData[section];
207   HeaderData::const_iterator i = d.find(role);
208 
209   if (i != d.end())
210     return i->second;
211   else
212     return cpp17::any();
213 }
214 
index(int row,int column,const WModelIndex & parent)215 WModelIndex WStandardItemModel::index(int row, int column,
216 				      const WModelIndex& parent) const
217 {
218   WStandardItem *parentItem = itemFromIndex(parent, false);
219 
220   if (parentItem
221       && row >= 0
222       && column >= 0
223       && row < parentItem->rowCount()
224       && column < parentItem->columnCount())
225     return createIndex(row, column, static_cast<void *>(parentItem));
226 
227   return WModelIndex();
228 }
229 
columnCount(const WModelIndex & parent)230 int WStandardItemModel::columnCount(const WModelIndex& parent) const
231 {
232   WStandardItem *parentItem = itemFromIndex(parent, false);
233 
234   return parentItem ? parentItem->columnCount() : 0;
235 }
236 
rowCount(const WModelIndex & parent)237 int WStandardItemModel::rowCount(const WModelIndex& parent) const
238 {
239   WStandardItem *parentItem = itemFromIndex(parent, false);
240 
241   return parentItem ? parentItem->rowCount() : 0;
242 }
243 
insertColumns(int column,int count,const WModelIndex & parent)244 bool WStandardItemModel::insertColumns(int column, int count,
245 				       const WModelIndex& parent)
246 {
247   WStandardItem *parentItem = itemFromIndex(parent); // lazy create ok
248 
249   if (parentItem)
250     parentItem->insertColumns(column, count);
251 
252   return parentItem;
253 }
254 
insertRows(int row,int count,const WModelIndex & parent)255 bool WStandardItemModel::insertRows(int row, int count,
256 				    const WModelIndex& parent)
257 {
258   WStandardItem *parentItem = itemFromIndex(parent); // lazy create ok
259 
260   if (parentItem)
261     parentItem->insertRows(row, count);
262 
263   return parentItem;
264 }
265 
removeColumns(int column,int count,const WModelIndex & parent)266 bool WStandardItemModel::removeColumns(int column, int count,
267 				       const WModelIndex& parent)
268 {
269   WStandardItem *parentItem = itemFromIndex(parent, false);
270 
271   if (parentItem)
272     parentItem->removeColumns(column, count);
273 
274   return parentItem;
275 }
276 
removeRows(int row,int count,const WModelIndex & parent)277 bool WStandardItemModel::removeRows(int row, int count,
278 				    const WModelIndex& parent)
279 {
280   WStandardItem *parentItem = itemFromIndex(parent, false);
281 
282   if (parentItem)
283     parentItem->removeRows(row, count);
284 
285   return parentItem;
286 }
287 
beginInsertColumns(const WModelIndex & parent,int first,int last)288 void WStandardItemModel::beginInsertColumns(const WModelIndex& parent,
289 					    int first, int last)
290 {
291   WAbstractItemModel::beginInsertColumns(parent, first, last);
292 
293   insertHeaderData(columnHeaderData_, columnHeaderFlags_, itemFromIndex(parent),
294 		   first, last - first + 1);
295 }
296 
beginInsertRows(const WModelIndex & parent,int first,int last)297 void WStandardItemModel::beginInsertRows(const WModelIndex& parent,
298 					 int first, int last)
299 {
300   WAbstractItemModel::beginInsertRows(parent, first, last);
301 
302   insertHeaderData(rowHeaderData_, rowHeaderFlags_, itemFromIndex(parent),
303 		   first, last - first + 1);
304 }
305 
beginRemoveColumns(const WModelIndex & parent,int first,int last)306 void WStandardItemModel::beginRemoveColumns(const WModelIndex& parent,
307 					    int first, int last)
308 {
309   WAbstractItemModel::beginRemoveColumns(parent, first, last);
310 
311   removeHeaderData(columnHeaderData_, columnHeaderFlags_, itemFromIndex(parent),
312 		   first, last - first + 1);
313 }
314 
beginRemoveRows(const WModelIndex & parent,int first,int last)315 void WStandardItemModel::beginRemoveRows(const WModelIndex& parent,
316 					 int first, int last)
317 {
318   WAbstractItemModel::beginRemoveRows(parent, first, last);
319 
320   removeHeaderData(rowHeaderData_, rowHeaderFlags_, itemFromIndex(parent),
321 		   first, last - first + 1);
322 }
323 
insertHeaderData(std::vector<HeaderData> & headerData,std::vector<WFlags<HeaderFlag>> & fl,WStandardItem * item,int index,int count)324 void WStandardItemModel::insertHeaderData(std::vector<HeaderData>& headerData,
325 					  std::vector<WFlags<HeaderFlag> >& fl,
326 					  WStandardItem *item, int index,
327 					  int count)
328 {
329   if (item == invisibleRootItem_.get()) {
330     headerData.insert(headerData.begin() + index, count, HeaderData());
331     fl.insert(fl.begin() + index, count, WFlags<HeaderFlag>());
332   }
333 }
334 
removeHeaderData(std::vector<HeaderData> & headerData,std::vector<WFlags<HeaderFlag>> & fl,WStandardItem * item,int index,int count)335 void WStandardItemModel::removeHeaderData(std::vector<HeaderData>& headerData,
336 					  std::vector<WFlags<HeaderFlag> >& fl,
337 					  WStandardItem *item, int index,
338 					  int count)
339 {
340   if (item == invisibleRootItem_.get()) {
341     headerData.erase(headerData.begin() + index,
342 		     headerData.begin() + index + count);
343     fl.erase(fl.begin() + index, fl.begin() + index + count);
344   }
345 }
346 
setData(const WModelIndex & index,const cpp17::any & value,ItemDataRole role)347 bool WStandardItemModel::setData(const WModelIndex& index,
348                                  const cpp17::any& value, ItemDataRole role)
349 {
350   WStandardItem *item = itemFromIndex(index);
351 
352   if (item)
353     item->setData(value, role);
354 
355   return item;
356 }
357 
setHeaderData(int section,Orientation orientation,const cpp17::any & value,ItemDataRole role)358 bool WStandardItemModel::setHeaderData(int section, Orientation orientation,
359                                        const cpp17::any& value, ItemDataRole role)
360 {
361   std::vector<HeaderData>& header
362     = (orientation == Orientation::Horizontal)
363     ? columnHeaderData_ : rowHeaderData_;
364 
365   HeaderData& d = header[section];
366 
367   if (role == ItemDataRole::Edit)
368     role = ItemDataRole::Display;
369 
370   d[role] = value;
371 
372   headerDataChanged().emit(orientation, section, section);
373 
374   return true;
375 }
376 
setHeaderFlags(int section,Orientation orientation,WFlags<HeaderFlag> flags)377 void WStandardItemModel::setHeaderFlags(int section, Orientation orientation,
378 					WFlags<HeaderFlag> flags)
379 {
380   std::vector<WFlags<HeaderFlag> >& fl
381     = (orientation == Orientation::Horizontal)
382     ? columnHeaderFlags_ : rowHeaderFlags_;
383 
384   fl[section] = flags;
385 }
386 
headerFlags(int section,Orientation orientation)387 WFlags<HeaderFlag> WStandardItemModel::headerFlags(int section,
388 						   Orientation orientation)
389   const
390 {
391   const std::vector<WFlags<HeaderFlag> >& fl
392     = (orientation == Orientation::Horizontal)
393     ? columnHeaderFlags_ : rowHeaderFlags_;
394 
395   if (section >= (int)fl.size())
396     return WFlags<HeaderFlag>();
397   else
398     return fl[section];
399 }
400 
toRawIndex(const WModelIndex & index)401 void *WStandardItemModel::toRawIndex(const WModelIndex& index) const
402 {
403   return static_cast<void *>(itemFromIndex(index));
404 }
405 
fromRawIndex(void * rawIndex)406 WModelIndex WStandardItemModel::fromRawIndex(void *rawIndex) const
407 {
408   return indexFromItem(static_cast<WStandardItem *>(rawIndex));
409 }
410 
setSortRole(ItemDataRole role)411 void WStandardItemModel::setSortRole(ItemDataRole role)
412 {
413   sortRole_ = role;
414 }
415 
sort(int column,SortOrder order)416 void WStandardItemModel::sort(int column, SortOrder order)
417 {
418   invisibleRootItem_->sortChildren(column, order);
419 }
420 
dropEvent(const WDropEvent & e,DropAction action,int row,int column,const WModelIndex & parent)421 void WStandardItemModel::dropEvent(const WDropEvent& e, DropAction action,
422 				   int row, int column,
423 				   const WModelIndex& parent)
424 {
425   // In case of a move within the model, we simply move the WStandardItem,
426   // this preserves the item-flags
427   WItemSelectionModel *selectionModel
428     = dynamic_cast<WItemSelectionModel *>(e.source());
429   if (selectionModel != 0 &&
430       selectionModel->model().get() == this &&
431       selectionModel->selectionBehavior() == SelectionBehavior::Rows &&
432       action == DropAction::Move) {
433     WModelIndexSet selection = selectionModel->selectedIndexes();
434     int r = row;
435     if (r < 0)
436       r = rowCount(parent);
437     WStandardItem *targetParentItem = itemFromIndex(parent);
438 
439     std::vector< std::vector<std::unique_ptr<WStandardItem> > > rows;
440     for (WModelIndexSet::const_iterator i = selection.begin();
441 	 i != selection.end(); ++i) {
442       WModelIndex sourceIndex = *i;
443 
444       // remove the row
445       if (sourceIndex.parent() == parent &&
446 	  sourceIndex.row() < r)
447 	r--;
448       WStandardItem* parentItem = itemFromIndex(sourceIndex.parent());
449       rows.push_back(parentItem->takeRow(sourceIndex.row()));
450     }
451 
452     for (unsigned i=0; i < rows.size(); i++) {
453       targetParentItem->insertRow(r+i, std::move(rows[i]));
454     }
455   } else {
456     WAbstractItemModel::dropEvent(e, action, row, column, parent);
457   }
458 }
459 
460 }
461 
462 #endif // DOXYGEN_ONLY
463