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